CF838C-Future Failure【dp,子集卷积】
正題
題目鏈接:https://www.luogu.com.cn/problem/CF838C
題目大意
一個字符串sss,兩個人輪流操作,每次每個人可以選擇刪掉一個字符或者重排列這個字符串,但是不能出現(xiàn)之前出現(xiàn)過的字符串,不能操作者輸。
求有多少個長度為nnn且字符集大小為kkk的字符串使得先手必勝。
1≤n≤250000,1≤k≤261\leq n\leq 250000,1\leq k\leq 261≤n≤250000,1≤k≤26
解題思路
顯然如果刪掉一個字符能使得先手必敗,那么先手必勝。如果不能,那么肯定會一直重排列這個字符串。
那么如果一個字符串的排列數(shù)是偶數(shù),那么先手可以切換先后手,所以先手必勝。否則先手需要考慮能否刪除一個字符使得先手必敗。
設第iii個字符的數(shù)量是aia_iai?,那么一個字符串可重排列的方案就是n!∏i=1kai!\frac{n!}{\prod_{i=1}^ka_i!}∏i=1k?ai?!n!?,并且如果刪除一個字符iii,那么排列方式將會乘上ain\frac{a_i}{n}nai??。
那么如果nnn是奇數(shù),肯定存在一個aia_iai?是奇數(shù),也就是說刪除一個iii后排列方式的奇偶性不變。所以如果一個nnn是奇數(shù)且字符串的排列數(shù)是奇數(shù),那么肯定可以刪除一個字符使得排列數(shù)仍然是偶數(shù)。
那么如果nnn是奇數(shù)且先手,那么肯定不會被逼到一個走動后先手必勝的位置,所以nnn是奇數(shù)先手必勝。
然后如果nnn是偶數(shù),那么如果排列方式是偶數(shù)那么先手必勝否則先手必敗。
然后考慮怎么計數(shù)nnn是偶數(shù)的情況。考慮到一個n!n!n!包含的222質(zhì)因數(shù)的個數(shù)為∑i=0?n2i?\sum_{i=0}\lfloor\frac{n}{2^i}\rfloor∑i=0??2in??,那么如果一個字符串的排列方式是偶數(shù)那么肯定有
∑i=0?n2i?=∑p=1k∑i=0?ap2i?\sum_{i=0}\left\lfloor\frac{n}{2^i}\right\rfloor=\sum_{p=1}^k\sum_{i=0}\left\lfloor\frac{a_p}{2^i}\right\rfloori=0∑??2in??=p=1∑k?i=0∑??2iap???
然后又因為假設我們考慮一直分解xxx出來,顯然∏i=1kai!\prod_{i=1}^ka_i!∏i=1k?ai?!的xxx肯定不會比n!n!n!中xxx多,同理分解2k2^k2k出來也是一樣的,所以它們每一個iii求出來的答案都是恰好相等的,即
?i∈N,?n2i?=∑p=1k?ap2i?\forall i\in N,\left\lfloor\frac{n}{2^i}\right\rfloor=\sum_{p=1}^k\left\lfloor\frac{a_p}{2^i}\right\rfloor?i∈N,?2in??=p=1∑k??2iap???
那么aia_iai?求和的時候二進制就不能有進位了,也就是說對于nnn中的每個111,aia_iai?都恰好有一個是111,nnn中的每一個000,aia_iai?這一位都是000。
也就是把nnn的二進制分成若干份aia_iai?,每一份的貢獻是1ai!\frac{1}{a_i!}ai?!1?,要求貢獻的乘積和。這個分出一個部分來的轉(zhuǎn)移其實就是子集卷積,所以我們跑kkk次子集卷積即可。
這樣跑有點慢,所以我們還需要快速冪優(yōu)化。
時間復雜度:O(klog?knlog?2n)O(k\log kn\log ^2n)O(klogknlog2n)
code
#include<cstdio> #include<cstring> #include<algorithm> #define lowbit(x) (x&-x) using namespace std; const int N=1<<19; int n,k,P,f[20][N],g[20][N],c[N],inv[N],fac[N]; void FWT(int *f,int n,int op){for(int p=2;p<=n;p<<=1)for(int k=0,len=p>>1;k<n;k+=p)for(int i=k;i<k+len;i++)(f[i+len]+=f[i]*op)%=P;return; } signed main() {scanf("%d%d%d",&n,&k,&P);int ans=1;for(int i=1;i<=n;i++)ans=1ll*ans*k%P;fac[0]=inv[0]=inv[1]=1;for(int i=2;i<N;i++)inv[i]=P-1ll*inv[P%i]*(P/i)%P;for(int i=1;i<N;i++)fac[i]=1ll*fac[i-1]*i%P,inv[i]=1ll*inv[i-1]*inv[i]%P;if(n&1)return printf("%d\n",ans)&0;f[0][0]=1;int m=1,lg=0;while(m<=n)m<<=1,lg++;for(int i=1;i<m;i++)c[i]=c[i-lowbit(i)]+1;for(int i=0;i<=n;i++)g[c[i]][i]=inv[i];for(int i=0;i<c[n];i++)FWT(g[i],m,1);FWT(f[0],m,1);while(k){if(k&1){for(int i=c[n];i>=0;i--){for(int x=0;x<m;x++)f[i][x]=1ll*f[i][x]*g[0][x]%P;for(int j=1;j<=i;j++)for(int x=0;x<m;x++)f[i][x]=(f[i][x]+1ll*f[i-j][x]*g[j][x])%P;}}for(int i=c[n];i>=0;i--){for(int x=0;x<m;x++)g[i][x]=(i?2ll:1ll)*g[0][x]*g[i][x]%P;for(int j=1;j<i;j++)for(int x=0;x<m;x++)g[i][x]=(g[i][x]+1ll*g[j][x]*g[i-j][x])%P;}k>>=1;}FWT(f[c[n]],m,-1);ans=(ans-1ll*f[c[n]][n]*fac[n]%P)%P;printf("%d\n",(ans+P)%P);return 0; }總結(jié)
以上是生活随笔為你收集整理的CF838C-Future Failure【dp,子集卷积】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 阴阳师食梦貘哪里多 阴阳师食梦貘位置
- 下一篇: 德玛西亚皇子叫什么名字