博弈论(奇偶考虑法)+计数+DP(判定转dp):CF838C
本文搬运自本人高中时期CSDN博客,若图片加载不出来,可到原文查看:https://blog.csdn.net/zhangtingxiqwq/article/details/133362167
首先题目有博弈,先分析一波最优策略(步骤:分析性质)。
两个人,所以显然考虑奇偶考虑法+递归考虑。
首先删就是使子问题-1,重新排列是在当前子问题里的。
一个串的排列是有限的,所以这里就可以上奇偶考虑法。如果有偶数种串,则必然是后手先“被迫“进入子问题(要算上初始情况)
考虑假设法:我们可以先假设进入子问题:
-
必赢。先手进!
-
必死。偶串时后手被迫进入,先手胜!
我们的奇偶考虑法证明了串方案wei偶数时先手必胜了!
考虑奇数种时先手能不能赢,同样假设一下:
-
进去必赢。先手胜
-
进去必输。先手被迫进入,后手胜
现在先手就不能再这层耗了,只能进入下一层了。然后结合上面的结论,只能进入子问题种类数是奇数时先手才有机会。
然后好像就卡住了…
然后回到题目看一看,发现问种类数,考虑dp太早了,就先想下计数
假设每种字符出现次数为 a ,那么就有 na 种串。然后我们现在这个是奇数。
考虑删掉一个变成什么,是 ∏a!(a−1)!n! ,我们现在希望这个是奇数。我们除一下发现上面要乘个 na ,则这个也要是奇数。
我们考虑我们还漏了什么条件, ∑a=n 。奇偶的话就从二进制的角度推敲一下, n 的最低位1必然存在在其中一个 a 里,所以 na 为奇数必然存在。
所以现在只和 n 的奇偶有关了。 n 偶先手必胜,否则必败。
剩下dp就很简单了。若 n 为奇数,我们要构造 na 为偶数,考虑用全局-奇。
因为有 ∏a!∣n! ,所以 ∏a! 的2的因子和 n! 只能相同。考虑类似10,不能用1+1表示,只能用10+0表示。所以每个 a 必然是 n 的子集。同时 ∑a=n
然后dp维护下 ∏a!1 的和。
有个小优化,就是钦定当前lowbit必选,最后乘个阶乘即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
| #include<bits/stdc++.h> using namespace std; #define int long long inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'|| ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;} #define Z(x) (x)*(x) #define pb push_back
#define N 250010
int mo; int pw(int a, int b) { int ans=1; while(b) { if(b&1) ans*=a; a*=a; b>>=1; ans%=mo; a%=mo; } return ans; } int fac[N], inv[N], ifac[N]; void init(int n) { int i; for(i=fac[0]=1; i<=n; ++i) fac[i]=fac[i-1]*i%mo; ifac[n]=pw(fac[n], mo-2); for(i=n-1; i>=0; --i) ifac[i]=ifac[i+1]*(i+1)%mo; for(i=1; i<=n; ++i) inv[i]=ifac[i]*fac[i-1]%mo; } int C(int n, int m) { if(m>n) return 0; return fac[n]*ifac[m]%mo*ifac[n-m]%mo; } int n, m, i, j, k, T; int f[27][N], s, t, ans;
void Add(int &a, int b) {
a+=b; if(a>=mo || a<=mo) a%=mo; }
int dfs(int i, int s) {
if(f[i][s]!=-1) return f[i][s]; if(i==0 || s==0) return 0; f[i][s]=0; int j=s&-s, t;
for(t=(s-j); ; t=(t-1)&(s-j)) { Add(f[i][s], dfs(i-1, s-j-t)*ifac[t+j]); if(!t) break; }
return f[i][s]; }
signed main() {
n=read(); k=read(); mo=read(); init(n); if(n%2) return printf("%lld\n", pw(k, n)), 0; memset(f, -1, sizeof(f)); f[0][0]=1;
for(i=1; i<=k; ++i) {
Add(ans, fac[n]*dfs(i, n)%mo*C(k, i)%mo*fac[i]%mo); }
Add(ans, pw(k, n)-2*ans); printf("%lld", (ans%mo+mo)%mo); return 0; }
|