Future Failure
题目大意
两个人对着一个字符串博弈,每次可以重新排列这个字符串或者删去一个字符。然后每次得到的字符串要是之前没有出现过的,谁无法操作谁就输了。 然后问你长度为 n 的字符串,只能用 k 种字符,有多少个串先手必胜。
思路
首先我们博弈一下,会发现一个状态能赢要么是它拿走一个存在一个子串能赢,要么就是它这个串的排列方式是偶数。
那考虑那排列方式列出来:
n
!
a
1
!
a
2
!
a
3
!
.
.
.
a
k
!
\dfrac{n!}{a_1!a_2!a_3!...a_k!}
a1?!a2?!a3?!...ak?!n!?(
a
i
a_i
ai? 表示第
i
i
i 个字符的出现次数) 那你考虑删了一个数会改变多少如果删的是第
i
i
i 个字符,那就是乘上
a
i
n
\dfrac{a_i}{n}
nai??
如果
n
n
n 是奇数,必定有一个
a
i
a_i
ai? 也是奇数,那你可以删使得奇偶不变。
然后会发现
n
n
n 是奇数直接必胜,因为如果删了能赢就删,否则就可以不停的排,那后手也一定不能删,也只能排,那会发现如果是不能删的情况一定是偶数中排列方式的。 (可以通过上面那个奇偶不变得到)
那如果是偶数就不能让自己走到奇数,所以就是只用它自己的排列方式是偶数。 那我们接下来就是考虑统计这个偶数的,发现不好统计,我们考虑统计奇数的,就是
n
!
n!
n! 质因数分解的每个
2
2
2 都可以跟下面的
2
2
2 对应。 那考虑统计
2
2
2 质因子的个数,先考虑一个阶乘怎么求。
n
!
→
∑
i
?
n
2
i
?
n!\rightarrow \sum\limits_{i}\left\lfloor\dfrac{n}{2^i}\right\rfloor
n!→i∑??2in??
∑
i
?
n
2
i
?
=
∑
x
=
1
k
∑
i
?
a
x
2
i
?
\sum\limits_{i}\left\lfloor\dfrac{n}{2^i}\right\rfloor=\sum\limits_{x=1}^k\sum\limits_{i}\left\lfloor\dfrac{a_x}{2^i}\right\rfloor
i∑??2in??=x=1∑k?i∑??2iax???
然后我们考虑不停分解
x
x
x 出来,因为上的数量肯定比下面的多,那我们就把这个情况看到每个
2
i
2^i
2i 中,这都应该是要满足的,所以它们每一位应该都是一样的:
?
i
∈
N
,
?
n
2
i
?
=
∑
i
?
a
x
2
i
?
\forall i\in N,\left\lfloor\dfrac{n}{2^i}\right\rfloor=\sum\limits_{i}\left\lfloor\dfrac{a_x}{2^i}\right\rfloor
?i∈N,?2in??=i∑??2iax??? 那你就考虑这个是什么鬼,那我们会发现这个其实跟二进制有关,就是所有
a
x
a_x
ax? 加在一起不能有进位。
那这个其实我们不难想到一个东西就是把
n
n
n 的二进制拆成若干份,每个
x
x
x 贡献是
1
x
!
\dfrac{1}{x!}
x!1?,然后每个的贡献乘起来,最后另外乘上
n
!
n!
n!。 然后你这个乘你可以用循环卷积来做,就
k
k
k 个字符依次加入然后 FWT 一下。 然后这样还是太慢所以可以
k
k
k 这个地方快速幂一下。
代码
#include<cstdio>
#define ll long long
using namespace std;
const int N = 1 << 19;
int n, k, mo, jc[N], inv[N], num1[N];
int f[20][N], g[20][N];
int jia(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int jian(int x, int y) {return x < y ? x - y + mo : x - y;}
int cheng(int x, int y) {return 1ll * x * y % mo;}
int ksm(int x, int y) {
int re = 1;
while (y) {
if (y & 1) re = cheng(re, x);
x = cheng(x, x); y >>= 1;
}
return re;
}
void Init() {
jc[0] = 1; for (int i = 1; i < N; i++) jc[i] = cheng(jc[i - 1], i);
inv[0] = inv[1] = 1; for (int i = 2; i < N; i++) inv[i] = cheng(inv[mo % i], (mo - mo / i));
for (int i = 1; i < N; i++) inv[i] = cheng(inv[i - 1], inv[i]);
for (int i = 1; i < N; i++) num1[i] = num1[i ^ (i & (-i))] + 1;
}
void FWT(int *f, int limit, int op) {
for (int mid = 1; mid < limit; mid <<= 1) {
for (int R = mid << 1, j = 0; j < limit; j += R) {
for (int k = 0; k < mid; k++) {
ll x = f[j | k], y = f[j | mid | k];
f[j | k] = x; f[j | mid | k] = jia(cheng(x, op), y);
}
}
}
}
int work(int n, int k) {
int m = 1; while (m <= n) m <<= 1;
f[0][0] = 1; for (int i = 0; i <= n; i++) g[num1[i]][i] = inv[i];
for (int i = 0; i < num1[n]; i++) FWT(g[i], m, 1);
FWT(f[0], m, 1);
for (int kk = k; kk; kk >>= 1) {
if (kk & 1) {
for (int i = num1[n]; i >= 0; i--) {
for (int j = 0; j < m; j++)
f[i][j] = cheng(f[i][j], g[0][j]);
for (int j = 0; j < i; j++)
for (int k = 0; k < m; k++) {
f[i][k] = jia(f[i][k], cheng(f[j][k], g[i - j][k]));
}
}
}
for (int i = num1[n]; i >= 0; i--) {
for (int j = 0; j < m; j++)
g[i][j] = cheng((i ? 2 : 1), cheng(g[i][j], g[0][j]));
for (int j = 1; j < i; j++)
for (int k = 0; k < m; k++) {
g[i][k] = jia(g[i][k], cheng(g[j][k], g[i - j][k]));
}
}
}
FWT(f[num1[n]], m, mo - 1);
return cheng(f[num1[n]][n], jc[n]);
}
int main() {
scanf("%d %d %d", &n, &k, &mo);
if (n & 1) {
printf("%d", ksm(k, n)); return 0;
}
Init();
printf("%d", jian(ksm(k, n), work(n, k)));
return 0;
}
|