初见安~这里是传送门:CF838C Future Failure
题解
博弈思路,可以发现当前是否要减少一个字符取决于当前的排列数量奇偶性。
(若当前有k种字符,各有个,共n个,那么排列数量就是:。)
可以发现,若当前状态有偶数种排列,那么我先手则最后一定是对方被迫删掉一个字符;反之奇数则是我被迫删去一个字符。
也就是说偶数的情况下,如果下一层后存在先手必败策略那么我可以主动删掉一个字符;若全是先手必胜策略那我可以消耗到对方被迫删除一个字符,对我就是必胜。
所以当前偶数种排列的话,一定先手必胜,反之必败。
若当前先手被迫需要进行一次删除操作,则相当于当前方案数为奇数,且方案数变为。
因为要尽量营造下一个状态为先手必败,所以一定选的是质因数分解后2的次数最小的那个让最后的奇偶性尽量为奇,换言之方案数中质因数分解后2的幂次不会增加。
这样的话,若当前n为奇数,则一定存在一个同样为奇数的使操作后2的幂次不变,方案数依旧是奇数。这就是一个真正的先手必败的状态。
所以若n为奇数,则一定先手必胜。
若n为偶数,则当前排列数为偶数才先手必胜。
所以现在的问题就是,n为偶数时,有多少种方案下排列数为偶数。
为偶数的条件是n!质因数分解后的2的个数大于每个ai!质因数分解后2的个数的和,所以不好判断,我们反过来算为奇数的排列数,也就是刚好等于。
在的限制下,我们真正的限制条件就是:
可以想到把n和ai都换成二进制表示,这样的话就是n二进制下每个为1的位都需要唯一一个ai在那一位为1,且n为0的二进制位a也必须为0。
正确性的话,因为,结合上文小于等于的限制,能取等当且仅当后没有进位,也就是没有出现新的二进制位为1。
至此我们已经可以dp了。因为每种选择都会对应一个方案数的计算,形如,
所以——设表示前i种颜色,当前n剩余j的方案数的和。(n!最后算)
枚举当前颜色,枚举当前剩余的状态,枚举该状态的子集,就可以dp了。
记得最后计算答案的时候选的x个颜色会带一个组合数系数。
上代码——
#include<algorithm>
#include<iostream>
#include<cstring>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 300005
using namespace std;
typedef long long ll;
const int mx = 3e5;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int n, k, mod;
int pw(int a, int b) {int res = 1; while(b) {if(b & 1) res = 1ll * res * a % mod; a = 1ll * a * a % mod, b >>= 1;} return res;}
int fac[maxn], inv[maxn], dp[30][maxn];
int C(int n, int m) {return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;}
signed main() {
n = read(), k = read(), mod = read();
fac[0] = inv[0] = 1;
for(int i = 1; i <= mx; i++) fac[i] = 1ll * fac[i - 1] * i % mod;
inv[mx] = pw(fac[mx], mod - 2);
for(int i = mx - 1; i; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
if(n & 1) printf("%d\n", pw(k, n));
else {
dp[0][n] = 1;
for(int i = 1; i <= k; i++) for(int s = n; s; s = (s - 1 & n)) if(dp[i - 1][s])
for(int j = s; j; j = (j - 1 & s)) //枚举子集
dp[i][s - j] = (1ll * dp[i][s - j] + 1ll * inv[j] * dp[i - 1][s] % mod) % mod;
ll ans = pw(k, n);
for(int i = 1; i <= k; i++) ans = (ans - 1ll * dp[i][0] * fac[n] % mod * C(k, i) % mod + mod) % mod;
printf("%lld\n", ans);
}
return 0;
}
迎评:)
——End——