\(S\)串長爲\(n\),字符集大小爲\(k\)c++
一次操做爲:取走\(S\)的任意一個字符或將\(S\)重排爲一個沒有出現過的字符\(S'\)優化
詢問有多少個\(S\)使得後手必勝,答案對\(P\)取模spa
$n \le 3 \times 10^5 , k \le 10^9 , 10^8 \le P \le 10^9+100 $code
part 1 博弈it
先手必敗當且僅當\(n\)爲偶數,且不一樣的重排字符個數爲奇數(難怪全部sample沒有一個n爲奇數)class
設每個字符出現的次數爲\(a_i\),不一樣的個數爲\((^{ \ \ \ \ \ n }_{a_1 \ a_2 \cdots \ a_k})\) im
對n=1顯然成立static
若是\(n\)爲偶數,\(n-1\)爲奇數集合
這時刪掉這個字符的人必定是必拜的di
若是不一樣個數爲偶數,刪掉一個字符的必定是後手,不然必定是先手
若是\(n\)爲奇數,刪去一個字符\(i\)至關於將方案乘以\(\frac{a_i}{n}\)
考慮重排數爲奇數,必定存在一個$a_i $爲奇數,先手操做這個數必勝
考慮重排數爲偶數,因爲\(n\)爲奇數,到時n-1的個數必定仍然爲偶數
這時刪掉字符的人輸,而刪掉字符的人必定是後手
因此結論成立
part 2 計數部分
問題變成求$(^{ n }_{a_1 a_2 \cdots a_k}) % 2 = 1 , \sum a_i =n $的個數
\((^n_{a_1 , \cdots , a_k})\) %P 不爲0當且僅當拆分紅P進制以後\(a1 \& \cdots \& a_k = 0\)
考慮集合冪級數\(F(x) = \sum \frac{1}{i!}\) ,至關於求\(F(x)\)子集卷積的\(k\)次冪
這能夠用全家桶的exp和ln優化
part 3 ln和exp
若是直接用\(nlog \ n\)的exp和ln,乘法長度會從18變成32,常數巨大
因此須要用\(n^2\)的多項式exp和ln(pty講述了一下推法)
考慮f和exp(f)的關係等價於無向連通圖的EGF和無向圖的EGF之間的關係
考慮暴力地遞推:
\[ \begin{align} &考慮G(x)爲連通圖個數的EGF,F(x)爲無向圖個數的EGF:\\ &考慮無向連通圖和無向圖之間的遞推\\ &\begin{cases} F_i = \frac{f_i}{i!} \ \ , \ \ G_i = \frac{g_i}{i!}\\ F = exp(G) \\ f_i = \sum_{j=1}^{i} (^i_{j-1}) g_i f_{i-j} \\ \end{cases} 化簡得:\\ &\begin{cases} G_0 = 0 \ , \ F_0 = 1\\ F_i = \frac{1}{i}\sum_{j=1}^{i} jG_jF_{i-j}\\ G_i = F_i - \frac{1}{i}\sum_{j=1}^{i-1}jG_jF_{i-j}\\ \end{cases} \end{align} \]
這樣就能夠作到\(n^2\)
時間複雜度\(O(n \ log ^2 n )\)
#include<bits/stdc++.h> #define ll long long #define il inline #define rg register using namespace std; const int N=1<<19,M=20; int n,m,mx,P,L,a[M][N],cnt[N],b[N],c[N],ny[N],fac[N],inv[N];ll lim; il void inc(int&x,int y){x+=y;if(x>=P)x-=P;} il void dec(int&x,int y){x-=y;if(x<0)x+=P;} il void fwt(int*A){ for(rg int i=1;i<L;i<<=1) for(rg int j=0;j<L;j+=i<<1) for(rg int k=0;k<i;++k){ inc(A[j+k+i],A[j+k]); } } il void ifwt(int*A){ for(rg int i=1;i<L;i<<=1) for(rg int j=0;j<L;j+=i<<1) for(rg int k=0;k<i;++k){ dec(A[j+k+i],A[j+k]); } } il void exp(int*A,int*B,int l){ B[0]=1; for(rg int i=1;i<=l;++i){ B[i]=0;ll t=0; for(rg int j=1;j<=i;++j){ //inc(B[i],1ll*j*A[j]%P*B[i-j]%P); t+=1ll*A[j]*B[i-j]%lim*j; if(t>=lim)t-=lim; } //B[i]=1ll*B[i]*ny[i]%P; B[i]=(t%P)*ny[i]%P; } } il void ln(int*A,int*B,int l){ B[0]=0; for(int i=1;i<=l;++i){ B[i]=0;ll t=0; for(rg int j=1;j<i;++j){ //dec(B[i],1ll*j*B[j]%P*A[i-j]%P); t+=1ll*B[j]*A[i-j]%lim*j; if(t>=lim)t-=lim; } //B[i]=(A[i]+1ll*B[i]*ny[i])%P; B[i]=(A[i]+(P-t%P)*ny[i])%P; } } il void pow(int*A,int k,int l){ static int t1[N]; ln(A,t1,l); for(int i=0;i<=l;++i)t1[i]=1ll*t1[i]*k%P; exp(t1,A,l); } int main(){ freopen("megalovania.in","r",stdin); freopen("megalovania.out","w",stdout); scanf("%d%d%d",&n,&m,&P); if(n&1)return puts("0"),0; lim=(ll)4e18/P*P; L=1;while(L<=n)L<<=1; ny[1]=1;for(int i=2;i<=n;++i)ny[i]=1ll*(P-P/i)*ny[P%i]%P; for(int i=fac[0]=inv[0]=1;i<=n;++i){ fac[i]=1ll*fac[i-1]*i%P; inv[i]=1ll*inv[i-1]*ny[i]%P; cnt[i]=cnt[i>>1]+(i&1); } mx=cnt[n]; for(int i=0;i<=n;++i)if((i|n)==n)a[cnt[i]][i]=inv[i]; for(int i=0;i<=mx;++i)fwt(a[i]); for(int i=0;i<=n;++i)if((i|n)==n){ for(int j=0;j<=mx;++j)b[j]=a[j][i]; pow(b,m,mx); c[i]=b[mx]; } ifwt(c); int ans=1ll*c[n]*fac[n]%P; cout<<ans<<endl; return 0; }