【2019 江苏省队集训】Day1 解题报告

【T1】 光影交错

【思路要点】

  • N N N 轮后仪式仍然进行的概率为 ( 1 − p ) N (1-p)^N (1p)N ,当 p = 1 0 − 5 p=10^{-5} p=105 时,取 N > 1 0 7 N>10^7 N>107 可以保证该概率在 1 0 − 20 10^{-20} 1020 以内,因此忽略这部分情况不会导致精度要求不能接受的误差产生。
  • f ( i ) f(i) f(i) 表示出现 i i i 次非中性灵力的情况出现的期望次数, g ( i ) g(i) g(i) 表示出现 i i i 点非中性灵力后阳性的灵力严格多于阴性灵力的概率,那么有 A n s = ∑ i = 1 + ∞ f ( i ) g ( i ) ≈ ∑ i = 1 N f ( i ) g ( i ) Ans=\sum_{i=1}^{+\infty}f(i)g(i)\approx\sum_{i=1}^{N}f(i)g(i) Ans=i=1+f(i)g(i)i=1Nf(i)g(i)
  • 关于 g ( x ) g(x) g(x) 的计算显然有
    g ( x ) = ∑ i = ⌊ x 2 ⌋ + 1 x ( x i ) p L i p D x − i g(x)=\sum_{i=\lfloor\frac{x}{2}\rfloor+1}^{x}\binom{x}{i}p_L^ip_D^{x-i} g(x)=i=2x+1x(ix)pLipDxi
    g ( x ) = { 0 x = 0 g ( x − 1 ) − p D × ( x − 1 x 2 ) p L x 2 p D x − 2 2 x ≡ 0   ( m o d   2 ) g ( x − 1 ) + p L × ( x − 1 x − 1 2 ) p L x − 1 2 p D x − 1 2 x ≡ 1   ( m o d   2 ) g(x)=\left\{ \begin{array}{rcl} 0 & & {x=0}\\ g(x-1)-p_D\times\binom{x-1}{\frac{x}{2}}p_L^{\frac{x}{2}}p_D^{\frac{x-2}{2}} & & {x\equiv0\ (mod\ 2)}\\ g(x-1)+p_L\times\binom{x-1}{\frac{x-1}{2}}p_L^{\frac{x-1}{2}}p_D^{\frac{x-1}{2}} & & {x\equiv1\ (mod\ 2)} \end{array} \right. g(x)=0g(x1)pD×(2xx1)pL2xpD2x2g(x1)+pL×(2x1x1)pL2x1pD2x1x=0x0 (mod 2)x1 (mod 2)
  • 递推需要用到的组合数,该部分时间复杂度为 O ( N ) O(N) O(N)
  • F ( x ) = ∑ i = 0 + ∞ f ( i ) x i F(x)=\sum_{i=0}^{+\infty}f(i)x^i F(x)=i=0+f(i)xi ,则有
    F ( x ) = ∑ i = 1 + ∞ ( 1 − p ) i − 1 [ ( p L + p D ) x + ( 1 − p L − p D ) ] i = 1 − p 1 − ( 1 − p ) [ ( p L + p D ) x + ( 1 − p L − p D ) ] F(x)=\sum_{i=1}^{+\infty}(1-p)^{i-1}[(p_L+p_D)x+(1-p_L-p_D)]^i=\frac{1-p}{1-(1-p)[(p_L+p_D)x+(1-p_L-p_D)]} F(x)=i=1+(1p)i1[(pL+pD)x+(1pLpD)]i=1(1p)[(pL+pD)x+(1pLpD)]1p
  • 因此对于 x ≥ 2 x\geq2 x2 f ( x ) f(x) f(x) 存在递推式 f ( x ) = f ( x − 1 ) × ( 1 − p ) ( p L + p D ) 1 − ( 1 − p ) ( 1 − p L − p D ) f(x)=\frac{f(x-1)\times(1-p)(p_L+p_D)}{1-(1-p)(1-p_L-p_D)} f(x)=1(1p)(1pLpD)f(x1)×(1p)(pL+pD)
  • 暴力计算 f ( 0 ) , f ( 1 ) f(0),f(1) f(0),f(1) ,递推即可,该部分时间复杂度同样为 O ( N ) O(N) O(N)
  • 时间复杂度 O ( N ) O(N) O(N) ,其中 N = 1 0 7 N=10^7 N=107

【代码】

#include
using namespace std;
const int MAXN = 1e7 + 5;
const double eps = 1e-12;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
double getans(double a, double b, double p) {
	static double tmp[MAXN], res[MAXN];
	int n = 0; tmp[0] = 1, res[0] = 0;
	tmp[1] = res[1] = 0;
	double ans = 0, now = 1;
	while (now > eps) {
		for (int i = 1; i >= 0; i--) {
			tmp[i] *= 1 - a - b;
			if (i != 0) tmp[i] += tmp[i - 1] * (a + b);
			res[i] += now * tmp[i];
		}
		n++, now *= 1 - p;
	}
	double tnp = 1 - (1 - p) * (1 - a - b);
	for (int i = 2; i <= n; i++)
		res[i] = res[i - 1] * (1 - p) * (a + b) / tnp;
	static double draw[MAXN], win[MAXN];
	double sum = a + b;
	a /= sum, b /= sum;
	for (int i = 0; i <= n; i++)
		if (i & 1) {
			win[i] = win[i - 1] + draw[i - 1] * a;
			if (i == 1) draw[i] = a;
			else draw[i] = draw[i - 2] * i * (i - 1) / (i / 2) / (i / 2 + 1) * a * b;
			ans += win[i] * res[i];
		} else {
			if (i == 0) draw[i] = 1;
			else draw[i] = 4 * draw[i - 2] * (i - 1) / i * a * b;
			if (i != 0) win[i] = win[i - 1] - draw[i - 1] * b;
			ans += win[i] * res[i];
		}
	return ans;
}
int main() {
	freopen("augury.in", "r", stdin);
	freopen("augury.out", "w", stdout);
	int num, T; read(num), read(T);
	while (T--) {
		double a, b, p;
		scanf("%lf%lf%lf", &a, &b, &p);
		printf("%.10lf\n", getans(a, b, p));
	}
	return 0;
}

【T2】 七星连珠

【思路要点】

  • 考虑 k = 2 k=2 k=2 的情况。
  • 将矩阵中的每一个元素看做集合幂级数,我们需要判断乘法定义为异或卷积的情况下矩阵积和式的每一项系数是否为 0 0 0
  • 积和式较为难求,但注意到我们只需要判断每一项系数是否为 0 0 0 ,我们可以将集合幂级数的每一项乘上一个随机系数,求行列式。求行列式的过程可以将矩阵中的每一个集合幂级数FWT,求矩阵每一位的行列式,再 UFWT 回去。
  • 时间复杂度 O ( N 3 × V + N 2 × V L o g V ) O(N^3\times V+N^2\times VLogV) O(N3×V+N2×VLogV)
  • 对于 k = 3 k=3 k=3的情况,我们需要实现一个三进制的 FWT 。
  • 回忆 FWT 异或变换的推导过程:
  • 我们定义 ⋅ · 表示集合幂级数的点积运算, ∗ * 表示集合幂级数的异或卷积运算。
  • 我们希望构造出一种可逆变换 FWT ,使得 F W T ( x 0 , x 1 ) ⋅ F W T ( y 0 , y 1 ) = F W T ( z 0 , z 1 ) FWT(x_0,x_1)·FWT(y_0,y_1)=FWT(z_0,z_1) FWT(x0,x1)FWT(y0,y1)=FWT(z0,z1) ,其中 ( z 0 , z 1 ) = ( x 0 , x 1 ) ∗ ( y 0 , y 1 ) = ( x 0 y 0 + x 1 y 1 , x 0 y 1 + x 1 y 0 ) (z_0,z_1)=(x_0,x_1)*(y_0,y_1)=(x_0y_0+x_1y_1,x_0y_1+x_1y_0) (z0,z1)=(x0,x1)(y0,y1)=(x0y0+x1y1,x0y1+x1y0) ,一旦构造出这样的变换,我们将其应用于全部的 N N N 位上,就可以完成 FWT 。
  • 我们不妨假设该变换为线性变换。
  • F W T ( x 0 , x 1 ) = ( a 0 , 0 x 0 + a 0 , 1 x 1 , a 1 , 0 x 0 + a 1 , 1 x 1 ) FWT(x_0,x_1)=(a_{0,0}x_0+a_{0,1}x_1,a_{1,0}x_0+a_{1,1}x_1) FWT(x0,x1)=(a0,0x0+a0,1x1,a1,0x0+a1,1x1) ,则有如下不定方程:
    ( a 0 , 0 x 0 + a 0 , 1 x 1 ) × ( a 0 , 0 y 0 + a 0 , 1 y 1 ) = a 0 , 0 ( x 0 y 0 + x 1 y 1 ) + a 0 , 1 ( x 0 y 1 + x 1 y 0 ) (a_{0,0}x_0+a_{0,1}x_1)\times(a_{0,0}y_0+a_{0,1}y_1)=a_{0,0}(x_0y_0+x_1y_1)+a_{0,1}(x_0y_1+x_1y_0) (a0,0x0+a0,1x1)×(a0,0y0+a0,1y1)=a0,0(x0y0+x1y1)+a0,1(x0y1+x1y0)
    ( a 1 , 0 x 0 + a 1 , 1 x 1 ) × ( a 1 , 0 y 0 + a 1 , 1 y 1 ) = a 1 , 0 ( x 0 y 0 + x 1 y 1 ) + a 1 , 1 ( x 0 y 1 + x 1 y 0 ) (a_{1,0}x_0+a_{1,1}x_1)\times(a_{1,0}y_0+a_{1,1}y_1)=a_{1,0}(x_0y_0+x_1y_1)+a_{1,1}(x_0y_1+x_1y_0) (a1,0x0+a1,1x1)×(a1,0y0+a1,1y1)=a1,0(x0y0+x1y1)+a1,1(x0y1+x1y0)
  • 我们发现对于变换矩阵 a i , j a_{i,j} ai,j ,每一行的元素满足的不定方程组实际上是一样的,我们不妨考虑一行的解,再将可行的解填回矩阵,使得矩阵行列式非零,即该变换存在逆变换即可。
  • 一种可行的变换是 a 0 = { 1 , 1 } , a 1 = { 1 , − 1 } a_0=\{1,1\},a_1=\{1,-1\} a0={1,1},a1={1,1} ,也就是我们所熟知的 FWT ,实际上,如果我们进行的变换是 a 0 = { 1 , − 1 } , a 1 = { 1 , 1 } a_0=\{1,-1\},a_1=\{1,1\} a0={1,1},a1={1,1} ,同样也能够得到 FWT 的效果。
  • 回到三进制的情况,我们希望构造出一种可逆变换 FWT ,使得 F W T ( x 0 , x 1 , x 2 ) ⋅ F W T ( y 0 , y 1 , y 2 ) = F W T ( z 0 , z 1 , z 2 ) FWT(x_0,x_1,x_2)·FWT(y_0,y_1,y_2)=FWT(z_0,z_1,z_2) FWT(x0,x1,x2)FWT(y0,y1,y2)=FWT(z0,z1,z2)
  • 其中 ( z 0 , z 1 , z 2 ) = ( x 0 , x 1 , x 2 ) ∗ ( y 0 , y 1 , y 2 ) = ( x 0 y 0 + x 1 y 2 + x 2 y 1 , x 0 y 1 + x 1 y 0 + x 2 y 2 , x 0 y 2 + x 1 y 1 + x 2 y 0 ) (z_0,z_1,z_2)=(x_0,x_1,x_2)*(y_0,y_1,y_2)=(x_0y_0+x_1y_2+x_2y_1,x_0y_1+x_1y_0+x_2y_2,x_0y_2+x_1y_1+x_2y_0) (z0,z1,z2)=(x0,x1,x2)(y0,y1,y2)=(x0y0+x1y2+x2y1,x0y1+x1y0+x2y2,x0y2+x1y1+x2y0)
  • 同样地,令 F W T ( x 0 , x 1 , x 2 ) = ( a 0 , 0 x 0 + a 0 , 1 x 1 + a 0 , 2 x 2 , a 1 , 0 x 0 + a 1 , 1 x 1 + a 1 , 2 x 2 , a 2 , 0 x 0 + a 2 , 1 x 1 + a 2 , 2 x 2 ) FWT(x_0,x_1,x_2)=(a_{0,0}x_0+a_{0,1}x_1+a_{0,2}x_2,a_{1,0}x_0+a_{1,1}x_1+a_{1,2}x_2,a_{2,0}x_0+a_{2,1}x_1+a_{2,2}x_2) FWT(x0,x1,x2)=(a0,0x0+a0,1x1+a0,2x2,a1,0x0+a1,1x1+a1,2x2,a2,0x0+a2,1x1+a2,2x2)
  • 我们可以类似地得到关于矩阵每一行的元素的一个不定方程。
  • { 1 , 1 , 1 } \{1,1,1\} {1,1,1} 显然是一组可行的解,但我们发现,在实数范围内,难以找到 3 3 3 组使得矩阵行列式不为 0 0 0 的解。
  • 但实际上我们并不一定需要实数解,不妨联想一下 FFT 的过程,我们令 a = e 2 π i 3 , b = e 4 π i 3 a=e^{\frac{2\pi i}{3}},b=e^{\frac{4\pi i}{3}} a=e32πi,b=e34πi ,即 1 1 1 的另外两个单位根,那么 { 1 , a , b } , { 1 , b , a } \{1,a,b\},\{1,b,a\} {1,a,b},{1,b,a} 将也是两组可行的解。并且,矩阵 { { 1 , 1 , 1 } , { 1 , a , b } , { 1 , b , a } } \{\{1,1,1\},\{1,a,b\},\{1,b,a\}\} {{1,1,1},{1,a,b},{1,b,a}} 的行列式非零,因此,我们找到了一个符合要求的可逆变换。
  • 至此,我们实现了一个三进制的 FWT,套用 k = 2 k=2 k=2 时的算法即可。
  • 时间复杂度 O ( N 3 × V + N 2 × V L o g V ) O(N^3\times V+N^2\times VLogV) O(N3×V+N2×VLogV)

【代码】

#include
using namespace std;
const int MAXN = 55;
const int MAXM = 2187;
const int MAXLOG = 20;
const int P = 1e9 + 9;
const int G = 13;
const int inv2 = (P + 1) / 2;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
unsigned seed;
int p, q, d, cnt, inv, bit[MAXLOG];
int n, k, res[MAXM], a[MAXN][MAXN][MAXM], b[MAXN][MAXN];
unsigned Rand() {
	seed = seed * seed + rand();
	return seed;
}
int power(int x, int y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
int gauss() {
	int f = 1, ans = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = i; j <= n; j++)
			if (b[j][i] != 0) {
				swap(b[i], b[j]);
				if (i != j) f = P - f;
			}
		if (b[i][i] == 0) return 0;
		ans = 1ll * ans * b[i][i] % P;
		int inv = power(b[i][i], P - 2);
		for (int j = i + 1; j <= n; j++) {
			int tmp = 1ll * b[j][i] * inv % P;
			for (int k = i; k <= n; k++)
				b[j][k] = (b[j][k] - 1ll * b[i][k] * tmp % P + P) % P;
		}
	}
	return 1ll * ans * f % P;
}
void FWT(int *a, int N) {
	for (int len = 2; len <= N; len <<= 1)
	for (int i = 0; i < N; i += len)
	for (int j = i, k = i + len / 2; k < i + len; j++, k++) {
		int tmp = (a[j] + a[k]) % P, tnp = (a[j] - a[k] + P) % P;
		a[j] = tmp, a[k] = tnp;
	}
}
void UFWT(int *a, int N) {
	for (int len = 2; len <= N; len <<= 1)
	for (int i = 0; i < N; i += len)
	for (int j = i, k = i + len / 2; k < i + len; j++, k++) {
		int tmp = (a[j] + a[k]) % P, tnp = (a[j] - a[k] + P) % P;
		a[j] = 1ll * tmp * inv2 % P, a[k] = 1ll * tnp * inv2 % P;
	}
}
void XYXU(int *a, int N) {
	for (int t = 1; t <= cnt; t++)
	for (int s = 0; s < N; s += bit[t]) {
		for (int i = s, j = s + bit[t - 1], k = s + 2 * bit[t - 1]; k < s + bit[t]; i++, j++, k++) {
			int ti = a[i], tj = a[j], tk = a[k];
			int tmp = (2ll * P + tj + tk - 2 * ti) % P * inv % P;
			a[i] = (ti - tmp + P) % P;
			tmp = 1ll * tmp * (p - 1) % P;
			a[j] = (0ll + tk - ti + P - tmp + P) % P * d % P;
			a[k] = (0ll + tj - ti + P - tmp + P) % P * d % P;
		}
	}
}
void XYXT(int *a, int N) {
	for (int t = 1; t <= cnt; t++)
	for (int s = 0; s < N; s += bit[t]) {
		for (int i = s, j = s + bit[t - 1], k = s + 2 * bit[t - 1]; k < s + bit[t]; i++, j++, k++) {
			int ti = a[i], tj = a[j], tk = a[k];
			a[i] = (0ll + ti + tj + tk) % P;
			a[j] = (0ll + ti + 1ll * tj * p + 1ll * tk * q) % P;
			a[k] = (0ll + ti + 1ll * tj * q + 1ll * tk * p) % P;
		}
	}
}
int main() {
	freopen("astrology.in", "r", stdin);
	freopen("astrology.out", "w", stdout);
	int num; read(num);
	read(n), read(k);
	if (k == 2) {
		int Max = 0;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
			read(b[i][j]);
			chkmax(Max, b[i][j]);
		}
		int N = 1;
		while (N <= Max) N <<= 1;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
			memset(a[i][j], 0, sizeof(a[i][j]));
			a[i][j][b[i][j]] = Rand() % P;
			FWT(a[i][j], N);
		}
		for (int val = 0; val <= N - 1; val++) {
			for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				b[i][j] = a[i][j][val];
			res[val] = gauss();
		}
		UFWT(res, N);
		for (int i = 0; i <= N - 1; i++)
			if (res[i]) printf("%d ", i);
		printf("\n");
	} else {
		p = power(G, (P - 1) / 3), q = 1ll * p * p % P;
		d = power((q - p + P) % P, P - 2), inv = power((p + q - 2), P - 2);
		int Max = 0;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
			read(b[i][j]);
			chkmax(Max, b[i][j]);
		}
		int N = 1; cnt = 0;
		while (N <= Max) N *= 3, cnt++;
		bit[0] = 1;
		for (int i = 1; i <= cnt; i++)
			bit[i] = bit[i - 1] * 3;
		for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++) {
			memset(a[i][j], 0, sizeof(a[i][j]));
			a[i][j][b[i][j]] = Rand() % P;
			XYXT(a[i][j], N);
		}
		for (int val = 0; val <= N - 1; val++) {
			for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				b[i][j] = a[i][j][val];
			res[val] = gauss();
		}
		XYXU(res, N);
		for (int i = 0; i <= N - 1; i++)
			if (res[i]) printf("%d ", i);
		printf("\n");
	}
	return 0;
}

【T3】 魔法咒语

【思路要点】

  • I ( α , k ) I(\alpha,k) I(α,k) 表示 α \alpha α 天后小X能够组成的咒语数, S ( N , k ) = ∑ α = 1 N I ( α , k ) S(N,k)=\sum_{\alpha=1}^{N}I(\alpha,k) S(N,k)=α=1NI(α,k),即答案。
  • 根据容斥原理,有 I ( α , k ) = f ( α , k ) − ∑ i = 0 α ( − 1 ) i ( α i ) × f ( α − i , k ) I(\alpha,k)=f(\alpha,k)-\sum_{i=0}^{\alpha}(-1)^{i}\binom{\alpha}{i}\times f(\alpha-i,k) I(α,k)=f(α,k)i=0α(1)i(iα)×f(αi,k)
  • 其中 f ( α , k ) f(\alpha,k) f(α,k) 表示 ∑ i = 0 k α i \sum_{i=0}^{k}\alpha^i i=0kαi ,可以通过等比数列求和计算。因此 S ( N , k ) = ∑ α = 1 N ( f ( α , k ) − ∑ i = 0 α ( − 1 ) i ( α i ) × f ( α − i , k ) ) S(N,k)=\sum_{\alpha=1}^{N}(f(\alpha,k)-\sum_{i=0}^{\alpha}(-1)^{i}\binom{\alpha}{i}\times f(\alpha-i,k)) S(N,k)=α=1N(f(α,k)i=0α(1)i(iα)×f(αi,k))
    S ( N , k ) = ∑ α = 1 N f ( α , k ) − ∑ α = 1 N α ! ∑ i = 0 α ( − 1 ) i i ! × f ( α − i , k ) ( α − i ) ! S(N,k)=\sum_{\alpha=1}^{N}f(\alpha,k)-\sum_{\alpha=1}^{N}\alpha!\sum_{i=0}^{\alpha}\frac{(-1)^{i}}{i!}\times \frac{f(\alpha-i,k)}{(\alpha-i)!} S(N,k)=α=1Nf(α,k)α=1Nα!i=0αi!(1)i×(αi)!f(αi,k)
  • 直接通过 FFT 计算上式,时间复杂度 O ( N L o g N + N L o g k ) O(NLogN+NLogk) O(NLogN+NLogk),期望得分 55 ∼ 60 55\sim{}60 5560 分。
  • 同时注意到当 N = 1 0 7 N=10^7 N=107,我们需要能够支持次数在 2 25 2^{25} 225 级别的多项式乘法,传统的模数 998244353 998244353 998244353 是不行的。
  • 重新考虑问题,将 α \alpha α 的枚举范围改为 0 ∼ N 0\sim{} N 0N ,对 S ( N , k ) = ∑ α = 0 N ( f ( α , k ) − ∑ i = 0 α ( − 1 ) i ( α i ) × f ( α − i , k ) ) S(N,k)=\sum_{\alpha=0}^{N}(f(\alpha,k)-\sum_{i=0}^{\alpha}(-1)^{i}\binom{\alpha}{i}\times f(\alpha-i,k)) S(N,k)=α=0N(f(α,k)i=0α(1)i(iα)×f(αi,k)) 交换求和顺序,将 f ( α − i , k ) f(\alpha-i,k) f(αi,k) 提至外层,有
    S ( N , k ) = ∑ α = 0 N f ( α , k ) − ∑ i = 0 N f ( i , k ) ∑ α = i N ( − 1 ) α − i ( α i ) S(N,k)=\sum_{\alpha=0}^{N}f(\alpha,k)-\sum_{i=0}^{N}f(i,k)\sum_{\alpha=i}^{N}(-1)^{\alpha-i}\binom{\alpha}{i} S(N,k)=α=0Nf(α,k)i=0Nf(i,k)α=iN(1)αi(iα)
  • G ( i , N ) = ∑ α = i N ( − 1 ) α − i ( α i ) G(i,N)=\sum_{\alpha=i}^{N}(-1)^{\alpha-i}\binom{\alpha}{i} G(i,N)=α=iN(1)αi(iα) ,则 S ( N , k ) = ∑ α = 0 N f ( α , k ) − ∑ i = 0 N f ( i , k ) × G ( i , N ) S(N,k)=\sum_{\alpha=0}^{N}f(\alpha,k)-\sum_{i=0}^{N}f(i,k)\times G(i,N) S(N,k)=α=0Nf(α,k)i=0Nf(i,k)×G(i,N)
  • ( α i ) = ( α − 1 i − 1 ) + ( α − 1 i ) \binom{\alpha}{i}=\binom{\alpha-1}{i-1}+\binom{\alpha-1}{i} (iα)=(i1α1)+(iα1) ,可以得到 ( − 1 ) α − i ( α i ) = ( − 1 ) α − i ( α + 1 i + 1 ) + ( − 1 ) α − i − 1 ( α i + 1 ) (-1)^{\alpha-i}\binom{\alpha}{i}=(-1)^{\alpha-i}\binom{\alpha+1}{i+1}+(-1)^{\alpha-i-1}\binom{\alpha}{i+1} (1)αi(iα)=(1)αi(i+1α+1)+(1)αi1(i+1α)
  • 因此,有 G ( i , N ) = 2 × G ( i + 1 , N ) + ( − 1 ) N − i ( N + 1 i + 1 ) G(i,N)=2\times G(i+1,N)+(-1)^{N-i}\binom{N+1}{i+1} G(i,N)=2×G(i+1,N)+(1)Ni(i+1N+1)
  • 直接据此计算,时间复杂度 O ( N L o g k ) O(NLogk) O(NLogk) ,复杂度瓶颈在快速幂处。
  • 用线性筛预处理快速幂的结果,复杂度可降至 O ( N ) O(N) O(N) ,期望得分 100 100 100 分。

【代码】

#include
using namespace std;
const int MAXN = 1e7 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
int P, fac[MAXN], inv[MAXN];
int power(int x, ll y) {
	if (y == 0) return 1;
	int tmp = power(x, y / 2);
	if (y % 2 == 0) return 1ll * tmp * tmp % P;
	else return 1ll * tmp * tmp % P * x % P;
}
int binom(int x, int y) {
	if (y > x) return 0;
	else return 1ll * fac[x] * inv[y] % P * inv[x - y] % P;
}
int tot, prime[MAXN], Min[MAXN], kpow[MAXN];
void init(int n, ll k) {
	fac[0] = 1;
	for (int i = 1; i <= n; i++)
		fac[i] = 1ll * fac[i - 1] * i % P;
	inv[n] = power(fac[n], P - 2);
	for (int i = n - 1; i >= 0; i--)
		inv[i] = inv[i + 1] * (i + 1ll) % P;
	kpow[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (Min[i] == 0) {
			prime[++tot] = Min[i] = i;
			kpow[i] = power(i, k);
		} else kpow[i] = 1ll * kpow[i / Min[i]] * kpow[Min[i]] % P;
		for (int j = 1; j <= tot && prime[j] <= Min[i]; j++) {
			int tmp = prime[j] * i;
			if (tmp > n) break;
			Min[tmp] = prime[j];
		}
	}
}
void update(int &x, int y) {
	x += y;
	if (x >= P) x -= P;
}
int calc(int base, ll k) {
	if (base == 0) return 1;
	if (base == 1) return (k + 1) % P;
	return 1ll * (kpow[base] - 1) * inv[base - 1] % P * fac[base - 2] % P;
}
int f[MAXN], g[MAXN];
int main() {
	freopen("abracadabra.in", "r", stdin);
	freopen("abracadabra.out", "w", stdout);
	int num; read(num);
	int n, ans = 0; ll k;
	read(n), read(k), read(P);
	init(n + 1, k + 1);
	for (int i = 0; i <= n; i++) {
		f[i] = calc(i, k);
		update(ans, f[i]);
	}
	g[n] = 1;
	for (int i = n - 1; i >= 0; i--) {
		g[i] = 2ll * g[i + 1] % P;
		if ((n - i) & 1) update(g[i], P - binom(n + 1, i + 1));
		else update(g[i], binom(n + 1, i + 1));
	}
	for (int i = 0; i <= n; i++)
		update(ans, P - 1ll * f[i] * g[i] % P);
	writeln(ans);
	return 0;
}

你可能感兴趣的:(【类型】做题记录,【资料】好题,【算法】概率与期望,【算法】生成函数,【算法】容斥原理,【算法】FFT与NTT,【算法】FWT,【算法】组合数学)