[UOJ#214]合唱队形
来源:互联网 发布:php如何加密源码软件 编辑:程序博客网 时间:2024/06/06 07:34
题目大意
有n个人每个人有一个字符集初始为空。
有tot个事件,每个事件形如在第ai个人的字符集里放了bi这个小写字母。
每个时刻等概率发生一个事件。
有一个长度为m的字符串s。某个时刻如果存在编号连续的m个人,按从小到大的顺序发现对于第i个人字符集里都有si,那么则胜利。
问胜利的期望时间,要求判断无解,答案在模意义下进行。
容斥
设
答案是
考虑容斥,枚举哪些位置作为起点在时刻t匹配成功。
h(i)表示i这个二进制状态内的起点匹配所需要发生的事件总数。
g(i,t)表示长度为t的序列,每个位置是1~tot的数,有i个数一定要出现,概率是多少。
g的计算当然可以容斥
发现f的求和最终变成这个地方对
如果是真分数求和当然很好做。
注意j可以为0,那么会出现
只考虑
容易发现因为
如果没有发现这个,我们还可以换容斥方法。
g(i,t)的意义改为,长度为t的序列,每个位置是1~tot的数,有i个数一定不要全部出现,概率是多少。
此时
可以发现现在就不带
新的式子可以考虑每种情况被计算的次数证明正确性。
回到原来的地方。
然后注意
这个做法的时间复杂度是
对于m较小的情况,不妨设dp[i,s,k]表示确定到第i个位置,(i-m,i)里成为起点的二进制状态为s,带权方案数是多少(因为注意最后有-1的系数)。那么也很好做了,复杂度是
#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(); }}
- [UOJ#214]合唱队形
- UOJ#214. 【UNR #1】合唱队形
- [期望] UOJ #214. 【UNR #1】合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 合唱队形
- 机器学习储备(7):numpy一维数组和矩阵
- 机器学习决策树:sklearn分类和回归
- 机器学习储备(9):matplotlib绘图原理及实例
- 机器学习逻辑回归:原理解析及代码实现
- 机器学习储备(8):numpy之linspace 和 logspace
- [UOJ#214]合唱队形
- eCos移植到Cortex-M7执行swi 0导致异常
- 机器学习逻辑回归:算法兑现为python代码
- Gradle命令和配置
- 事务
- 1、计算机基础和linux发行版
- 替换空格:
- 设置mongoDB dos窗口数据库命令格式
- MyBatis的级联查询(association 进行分布查询)