[UOJ#214]合唱队形

来源:互联网 发布:php如何加密源码软件 编辑:程序博客网 时间:2024/06/06 07:34

题目大意

有n个人每个人有一个字符集初始为空。
有tot个事件,每个事件形如在第ai个人的字符集里放了bi这个小写字母。
每个时刻等概率发生一个事件。
有一个长度为m的字符串s。某个时刻如果存在编号连续的m个人,按从小到大的顺序发现对于第i个人字符集里都有si,那么则胜利。
问胜利的期望时间,要求判断无解,答案在模意义下进行。

容斥

f(i)表示时刻i还没有胜利的概率。
答案是i>=0f(i)
考虑容斥,枚举哪些位置作为起点在时刻t匹配成功。
f(t)=2nm+11i=0g(h(i),t)(1)bitcount(i)
h(i)表示i这个二进制状态内的起点匹配所需要发生的事件总数。
g(i,t)表示长度为t的序列,每个位置是1~tot的数,有i个数一定要出现,概率是多少。
g的计算当然可以容斥
g(i,t)=ij=0Cji(totjtot)t(1)j
发现f的求和最终变成这个地方对(totjtot)t的求和。
如果是真分数求和当然很好做。
注意j可以为0,那么会出现1t求和,怎么办?
只考虑1t最终的系数那么是nm+1i=0Cinm+1(1)i
容易发现因为n>=m这个式子的值是0。
如果没有发现这个,我们还可以换容斥方法。
f(t)=2nm+11i=1g(h(i),t)(1)bitcount(i)+1
g(i,t)的意义改为,长度为t的序列,每个位置是1~tot的数,有i个数一定不要全部出现,概率是多少。
此时g(i,t)=ij=1Cji(totjtot)t(1)j+1
可以发现现在就不带1t的项了。
新的式子可以考虑每种情况被计算的次数证明正确性。
回到原来的地方。
然后注意t>=0xt=101x,这个可能涉及求逆元,请进行预处理避免复杂度多带一个log。
这个做法的时间复杂度是O(2nmtot),只有m较大才能用。
对于m较小的情况,不妨设dp[i,s,k]表示确定到第i个位置,(i-m,i)里成为起点的二进制状态为s,带权方案数是多少(因为注意最后有-1的系数)。那么也很好做了,复杂度是O(2mntot),当然因为我好像毛都没预处理所以我的程序是O(2mnmtot)

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)#define two(i) (1<<(i))using namespace std;typedef long long ll;const int maxn=30+2,mo=998244353;char s[maxn],h[maxn];bool dis[maxn][maxn];int b[maxn][maxn],d[maxn],cnt[maxn*maxn],niy[maxn*maxn];int c[maxn*maxn][maxn*maxn],f[2][two(15)][maxn*26];int i,j,k,l,r,t,n,m,now,tot,ni,ans,ca;bool czy;int qsm(int x,int y){    if (!y) return 1;    int t=qsm(x,y/2);    t=(ll)t*t%mo;    if (y%2) t=(ll)t*x%mo;    return t;}bool pd(){    int i,j;    fo(i,1,n-m+1){        fo(j,1,m+1)            if (j>m||!dis[i+j-1][s[j]-'a']) break;        if (j>m) return 1;    }    return 0;}int calc(int x){    if (x==1) return 0;    else return qsm(1-x,mo-2);}int g(int cnt){    int i,l,t=0;    fo(i,0,cnt){        l=(ll)c[cnt][i]*/*calc((ll)(tot-i)*ni%mo)%mo*/niy[i]%mo;        if (i%2) (t-=l)%=mo;else (t+=l)%=mo;    }    return t;}void dfs(int x,int f,int y){    if (x==n-m+2){        (ans+=f*g(y))%=mo;        return;    }    dfs(x+1,f,y);    int i,t=0;    bool czy=1;    fo(i,x,x+m-1)        if (!dis[i][s[i-x+1]-'a']){            czy=0;            break;        }        else{            b[i][s[i-x+1]-'a']++;            if (b[i][s[i-x+1]-'a']==1) t++;        }    if (czy){        dfs(x+1,-f,y+t);        fo(i,x,x+m-1) b[i][s[i-x+1]-'a']--;    }}void brute(){    ans=0;    dfs(1,1,0);    (ans+=mo)%=mo;    printf("%d\n",ans);}void solve(){    fo(i,0,1)        fo(j,0,two(m-1)-1)            fo(k,0,tot) f[i][j][k]=0;    f[now=1][0][0]=1;    fo(i,1,n){        fo(j,0,two(m-1)-1)            fo(k,0,tot) f[1-now][j][k]=0;        fo(j,0,two(m-1)-1)            fo(k,0,tot)                if (f[now][j][k]){                    fo(t,0,25) d[t]=0;                    r=0;                    czy=1;                    l=j;                    fd(t,m,2){                        if (l%2==1){                            if (!dis[i][s[t]-'a']){                                czy=0;                                break;                            }                            d[s[t]-'a']++;                            if (d[s[t]-'a']==1) r++;                        }                        l/=2;                    }                    if (!czy) continue;                    (f[1-now][j/2][k+r]+=f[now][j][k])%=mo;                    if (i+m-1<=n&&dis[i][s[1]-'a']){                        if (!d[s[1]-'a']) r++;                        if (m==1) (f[1-now][0][k+r]-=f[now][j][k])%=mo;                        else (f[1-now][j/2+two(m-2)][k+r]-=f[now][j][k])%=mo;                    }                }        now=1-now;    }    fo(k,0,tot) cnt[k]=0;    fo(j,0,two(m-1)-1)        fo(k,0,tot) (cnt[k]+=f[now][j][k])%=mo;    ans=0;    fo(k,0,tot) (ans+=(ll)cnt[k]*g(k)%mo)%=mo;    (ans+=mo)%=mo;    printf("%d\n",ans);}int main(){    //freopen("ex_B4.in","r",stdin);    scanf("%d",&ca);    while (ca--){        scanf("%d%d",&n,&m);        fo(i,1,n)            fo(j,0,25) dis[i][j]=0;        tot=0;        fo(i,1,n){            scanf("%s",h+1);            t=strlen(h+1);            tot+=t;            fo(j,1,t) dis[i][h[j]-'a']=1;        }        scanf("%s",s+1);        if (!pd()){            printf("-1\n");            continue;        }        ni=qsm(tot,mo-2);        fo(i,1,tot) niy[i]=qsm(1-(ll)(tot-i)*ni%mo,mo-2);        c[0][0]=1;        fo(i,1,tot){            c[i][0]=1;            fo(j,1,i) c[i][j]=(c[i-1][j]+c[i-1][j-1])%mo;        }        if (n-m+1<=16) brute();        else solve();    }}
原创粉丝点击