【Ynoi2016】谁的梦 题解(容斥+STL)
9月份的时候不知道哪个毒瘤把这道题安排成了**数学专题**的作业题,我感觉这跟数学一点关系也没有……
题目大意:定义一个序列的权值为不同数字的个数。现在有$n$个序列,我们在每个序列里选一个连续非空子串拼接起来,求所有选法得到的序列权值之和。
首先考虑容斥(基本套路),考虑每个颜色对答案的贡献,用合法的减去不合法的。设长度为$n$的序列共有$G(n)$个子区间,显然$G(n)=\frac{n(n+1)}{2}$。设颜色$c$的总贡献为$sum_c$,则有$sum_c=\prod\limits_{i=1}^n G(len_i)-\prod\limits_{i=1}^n ans_{c,i}$。其中$ans_{c,i}$表示第$i$个序列里不包含颜色$c$的子区间个数。
于是问题就变成如何求$ans_{c,i}$。显然如果一个区间不包含颜色$c$,那么这个区间的左右端点一定在两个$c$之间。可以看成颜色$c$将一个序列分成$m$个长度为$l_i$的子序列。则有$ans_{c,i}=\sum\limits_{i=1}^m G(l_i)$。设总答案为$ANS$,颜色数为$cnt$,则$ANS=\sum\limits_{i=1}^{cnt} \prod\limits_{i=1}^n G(len_i)-\sum\limits_{i=1}^{cnt} \prod\limits_{i=1}^n ans_{c,i}$。
接下来是实现的问题,毒瘤STL。用一个数组存每种数有多少区间不包含,用STL::map存每个序列里每种数有多少区间不包含。对每一种数开一个STL::set存位置。记得要将数离散化!!!
一点细节:当序列里只有一种数的时候map值为0,这时候要特判一下。打标记处理即可。
代码:
#include<map> #include<set> #include<vector> #include<cstdio> #include<iostream> #include<algorithm> #define int long long using namespace std; typedef pair<int,int> pii; const int p=19260817; const int N=200005; int a[N],len[N],L[N],R[N],bl[N],inv[N*2],sum[N],P[N],flag[N]; int tot=1,n,m,ans; set<int> s[N]; map<pii,int> mp; vector<int> v; struct ask{ int x,y,z; }q[N]; inline int read() { int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } template<typename F> inline void write(F x, char ed = '\n') { static short st[30];short tp=0; if(x<0) putchar('-'),x=-x; do st[++tp]=x%10,x/=10; while(x); while(tp) putchar('0'|st[tp--]); putchar(ed); } inline int G(int len){ return (len*(len-1)>>1)%p; } inline int INV(int i){ return (i<300000)?inv[i]:(p-p/i)*INV(p%i)%p; } inline void insert(int col,int pos) { int pre=*--s[col].lower_bound(pos),suf=*s[col].upper_bound(pos); pre=max(pre,L[bl[pos]]-1),suf=min(suf,R[bl[pos]]+1); const pii xx=make_pair(bl[pos],col); if (!mp.count(xx)) mp[xx]=G(len[bl[pos]]+1); int &v=mp[xx]; ans=(ans+(flag[col]?0:sum[col]))%p; sum[col]=sum[col]*INV(v)%p; v=(v-G(suf-pre)+G(pos-pre)+G(suf-pos)+p)%p; if (v) sum[col]=sum[col]*v%p; else ++flag[col]; ans=(ans-(flag[col]?0:sum[col])+p)%p; s[col].insert(pos); } inline void erase(int col,int pos) { int pre=*--s[col].lower_bound(pos),suf=*s[col].upper_bound(pos); pre=max(pre,L[bl[pos]]-1),suf=min(suf,R[bl[pos]]+1); const pii xx=make_pair(bl[pos],col); if (!mp.count(xx)) mp[xx]=G(len[bl[pos]]+1); int &v=mp[xx]; ans=(ans+(flag[col]?0:sum[col]))%p; if (!v) --flag[col]; else sum[col]=sum[col]*INV(v)%p; v=(v+G(suf-pre)-G(pos-pre)-G(suf-pos)+p+p)%p; sum[col]=sum[col]*v%p; ans=(ans-(flag[col]?0:sum[col])+p)%p; s[col].erase(pos); } signed main() { n=read();m=read(); inv[1]=1; for (int i=2;i<300000;i++) inv[i]=(p-p/i)*inv[p%i]%p; for (int i=1;i<=n;i++) { len[i]=read(); L[i]=R[i-1]+1,R[i]=R[i-1]+len[i]; tot=tot*G(len[i]+1)%p;P[i]=P[i-1]+len[i]; } for (int i=1;i<=n;i++) for (int j=L[i];j<=R[i];j++) bl[j]=i,a[j]=read(),v.push_back(a[j]); for (int i=1;i<=m;i++){ q[i].x=read(),q[i].y=read(),q[i].z=read(); v.push_back(q[i].z); } sort(v.begin(),v.end()); v.erase(unique(v.begin(),v.end()),v.end()); for (int i=0;i<v.size();i++) s[i].insert(0),s[i].insert(R[n]+1),sum[i]=tot; for (int i=1;i<=R[n];i++){ a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin(); insert(a[i],i); } write(ans); for (int i=1;i<=m;i++) { const int id=P[q[i].x-1]+q[i].y; erase(a[id],id); a[id]=lower_bound(v.begin(),v.end(),q[i].z)-v.begin(); insert(a[id],id); write(ans); } return 0; }