[DS记录]P4692 [Ynoi2016] 谁的梦

command_block

2021-06-30 14:44:29

Personal

**题意** : 定义一个序列的权值为不同数字的个数。 给出 $n$ 个序列 $A_{1\sim n}$ ,在每个序列中选择一个连续非空子串,拼接起来。 求所有选法得到的序列的权值总和。如果一个序列能通过多种方法被选择出来,要计算多次。 需要支持单点修改。答案对 $19260817$ 取模。 允许离线,$m,\sum |A|\leq 10^5$ ,时限$\texttt{1.5s}$。 ------------ 简单题。 将所有作为值的数字离散化。 先不考虑修改。 记不同的数的总数为 $n_0$ ,分别考虑每个数的贡献。 正难则反,对于每个数 $k$ ,计数其没有出现的所有选法。 总选法是 $\prod_{i=1}^n\frac{(|A_i|+1)|A_i|}{2}$ ,记 $C_i=\frac{(|A_i|+1)|A_i|}{2}$。 对于序列 $i$ ,记 $s_{i,k}$ 表示在 $i$ 中选一个不包含 $k$ 的区间的方案数。 则答案为 : $$p\prod_{i=1}^nC_i-\sum\limits_{k=1}^{n_0}\prod_{i=1}^ns_{i,k}$$ 然而 $n,n_0$ 可能都很大,不能处理到每个 $s$。 注意到当 $A_i$ 中有数 $k$ 时, $s_{i,k}$ 才会 $\neq C_i$ 。这类特殊位置的个数是 $O\big(\sum |A|\big)$ 的。 记 $P_k=\prod_{i=1}^ns_{i,k}$。初始时令 $P_k=\prod_{i=1}^nC_i$。 逐个考虑 $k$ 出现过的序列的贡献,将 $P_k$ 除去 $C_i$ 后,乘上特殊计算的 $s_{i,k}$。 接下来考虑如何动态维护。 我们单点修改序列 $i$ 后,会影响两个 $s_{i,k}$ 的计算。 我们需要支持 : 插入,删除,计算不包含关键位置的数的个数。 注意到答案与极长不含关键点连续段有关,用 `std::set` 维护即可。 为了方便,可以将 $n$ 个序列拼在一起,这样每个颜色就只需要开一个 `set` 了。 对于那些不等于 $C_i$ 的 $s_{i,k}$ ,用 `std::map` 维护。 注意可能有某个 $s_{i,k}=0$ ,此时不便于做除法,要特殊记零因子的个数。 复杂度 $O(n\log n)$。 ```cpp #include<algorithm> #include<cstdio> #include<set> #include<map> #define itor set<int>::iterator #define Itor map<int,int>::iterator #define sec second #define ll long long #define MaxN 100500 using namespace std; const int mod=19260817; ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } inline int C(int n){return 1ll*n*(n+1)/2%mod;} set<int> p[MaxN<<1]; map<int,int> s[MaxN<<1]; int buf[MaxN<<1],c0[MaxN<<1],ret ,tl[MaxN],tr[MaxN],tb[MaxN],len[MaxN]; void add(int i,int c) { p[c].insert(i); itor it=p[c].find(i); int l,r,k=tb[i]; l=max(*(--it),tl[i]);it++; r=min(*(++it),tr[i]); if (!s[c].count(k)) s[c][k]=C(len[k]-len[k-1]); Itor it2=s[c].find(k); ret=(ret+(c0[c] ? 0 : buf[c]))%mod; buf[c]=1ll*buf[c]*powM(it2->sec)%mod; it2->sec=((ll)it2->sec+C(r-i-1)+C(i-l-1)-C(r-l-1))%mod; if (it2->sec==0)c0[c]++; else buf[c]=1ll*buf[c]*(it2->sec)%mod; ret=(ret-(c0[c] ? 0 : buf[c]))%mod; } void del(int i,int c) { itor it=p[c].find(i); int l,r,k=tb[i]; l=max(*(--it),tl[i]);it++; r=min(*(++it),tr[i]); p[c].erase(i); Itor it2=s[c].find(k); ret=(ret+(c0[c] ? 0 : buf[c]))%mod; if (it2->sec==0)c0[c]--; else buf[c]=1ll*buf[c]*powM(it2->sec)%mod; it2->sec=((ll)it2->sec+C(r-l-1)-C(r-i-1)-C(i-l-1))%mod; buf[c]=1ll*buf[c]*(it2->sec)%mod; ret=(ret-(c0[c] ? 0 : buf[c]))%mod; } int n,m,x[MaxN<<1],tn,to[MaxN],tx[MaxN],a[MaxN]; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) {scanf("%d",&len[i]);len[i]+=len[i-1];} for (int i=1;i<=n;i++) for (int j=len[i-1]+1;j<=len[i];j++){ scanf("%d",&a[j]);x[++tn]=a[j]; tl[j]=len[i-1];tr[j]=len[i]+1;tb[j]=i; } for (int i=1,id,pos;i<=m;i++){ scanf("%d%d%d",&id,&pos,&tx[i]); to[i]=len[id-1]+pos;x[++tn]=tx[i]; } sort(x+1,x+tn+1); tn=unique(x+1,x+tn+1)-x-1; ret=1; for (int i=1;i<=n;i++) ret=1ll*ret*C(len[i]-len[i-1])%mod; for (int i=1;i<=tn;i++){ buf[i]=ret; p[i].insert(0); p[i].insert(len[n]+1); }ret=0; for (int i=1;i<=len[n];i++){ a[i]=lower_bound(x+1,x+tn+1,a[i])-x; add(i,a[i]); } printf("%d\n",(ret+mod)%mod); for (int i=1;i<=m;i++){ int wfc=lower_bound(x+1,x+tn+1,tx[i])-x; del(to[i],a[to[i]]);add(to[i],a[to[i]]=wfc); printf("%d\n",(ret+mod)%mod); }return 0; } ```