【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;
}

 

posted @ 2020-11-03 19:28  我亦如此向往  阅读(121)  评论(0编辑  收藏  举报