CF961G Solution

CF961G Solution

这题好神啊 … 我发现我一直 WA5 的原因是开始统计和的时候没有取模(
然后就想着写篇题解记录一下 …

首先题目不是要求一个和吗。

那么通过人类智慧发现很显然所有数出现的次数都是一样的。
也就是说最后的和式必然是 $…\times \sum\limits_{i=1}^nw_i$ 这样的形式。

那么不妨将前面的系数设为 $p$。

我们对于这个问题其实可以用 DP 的思想来解决。
我们对于求整个柿子的系数,直接考虑枚举子集大小。
然后就可以发现我们当前选了 $i$ 个,然后算出自己集合的方案和别的集合的方案即可。
最后相乘累加就是答案,其中运用的是第二类斯特林数的思想。

第二类斯特林数就解决的是这样一类问题:

  • $n$ 个物品放入 $m$ 个不同集合中,求集合非空的总方案数。

想要研究的同学可以自行研究,这里不再赘述。

所以就可以得到系数 $p$ 变成了下面的柿子。

$$p=\sum\limits_{i=1}^ni\binom{n-1}{i-1}S_2(n-i,k-1)$$
$$=\sum\limits_{i=1}^ni\binom{n-1}{i-1}\dfrac{1}{(k-1)!}\sum\limits_{j=0}^{k-1}(-1)^j\binom{k-1}{j}(k-j-1)^{n-i}$$
$$=\sum\limits_{i=1}^ni\binom{n-1}{i-1}\dfrac{1}{(k-1)!}\sum\limits_{j=0}^{k-1}(-1)^j\dfrac{(k-1)!}{j!\times(k-j-1)!}(k-j-1)^{n-i}$$
$$=\sum\limits_{i=1}^ni\binom{n-1}{i-1}\sum\limits_{j=0}^{k-1}\dfrac{(-1)^j(k-j-1)^{n-i}}{j!\times(k-j-1)!}$$
$$=\sum\limits_{j=0}^{k-1}\dfrac{(-1)^j}{j!\times(k-j-1)!}\sum\limits_{i=1}^ni\binom{n-1}{i-1}(k-j-1)^{n-i}$$

注意上面将 $j$ 的和式提前了,这显然是正确的。
因为分母相同相加分母不变,然后 $-1$ 的系数显然只和 $j$ 有关。
但是分子剩下的部分 $(k-j-1)^{n-i}$ 显然和 $i$ 和 $j$ 都有关。
所以为了方便后面的推导,这里直接将 $j$ 提前作为后面那一堆和的系数。
这里说的 方便推导 其实也没有什么非常清晰的界定,只是这部分可以简单解决。
当然不排除有暴力老哥直接卷的情况。(
其实我也不知道可不可以卷,因为我没学多项式((

所以看到一大堆阶乘,在保证答案正确性的情况下,直接从一大堆柿子里面拎出来是必要的。

然后后面那个 $(k-j-1)$ 看起来特别丑,弄成 $s$。

$$\sum\limits_{i=1}^ni\binom{n-1}{i-1}s^{n-i}$$
$$=\sum\limits_{i=1}^ni\times s^{n-i}\dfrac{(n-1)!}{(i-1)! \times (n-i)!}$$

为了约分上面的组合数拆开了。

然后有一个 trick,
看到分母有一个 $i-1$,看到前面的系数 $i$,
所以把那个 $i$ 给拆掉,拆成 $i=i-1+1$。

所以有

$$=\sum\limits_{i=1}^ns^{n-i}\times(i-1+1)\dfrac{(n-1)!}{(i-1)! \times (n-i)!}$$
$$=\sum\limits_{i=1}^ns^{n-i}[(i-1)\times \dfrac{(n-1)!}{(i-1)! \times (n-i)!}+\dfrac{(n-1)!}{(i-1)! \times (n-i)!}]$$
$$=\sum\limits_{i=1}^ns^{n-i}(i-1)\dfrac{(n-1)!}{(i-1)! \times (n-i)!}+\sum\limits_{i=1}^ns^{n-i}\dfrac{(n-1)!}{(i-1)! \times (n-i)!}$$

然后约掉共同的 $i-1$。

$$=\sum\limits_{i=1}^ns^{n-i}\dfrac{(n-1)!}{(i-2)! \times (n-i)!}+\sum\limits_{i=1}^n\dfrac{(n-1)!}{(i-1)! \times (n-i)!}$$

再重新写成方便的组合数形式。

$$=\sum\limits_{i=1}^ns^{n-i}\binom{n-1}{i-2}+\sum_{i=1}^ns^{n-i}\binom{n-1}{i-1}$$

由于这个上下不统一所以把 $(n-1)$ 提出来。

$$=(n-1)\sum\limits_{i=1}^ns^{n-i}\binom{n-2}{i-2}+\sum\limits_{i=1}^ns^{n-i}\binom{n-1}{i-1}$$

然后发现这个玩意儿好像很不好算,
所以变成下面这样,因为 $\binom{n}{m}=\binom{n}{n-m}$。

$$=(n-1)\sum\limits_{i=1}^ns^{n-i}\binom{n-2}{n-i}+\sum\limits_{i=1}^ns^{n-i}\binom{n-1}{n-i}$$

然后根据那个什么柿子的展开,就是杨辉三角的系数,
观察到 $n-i$ 的共同部分可以运用上这个公式,
可以发现这个可以直接把组合数拆开变成幂。

实际上那个 $\sum\limits_{i=1}^n$ 有一些项应该是不能算的,因为组合数其实等于 $0$。
所以根据 $(a+b)^n$ 的系数其实就是组合数,可以直接代进去。

即 $(a+b)^n=\sum\limits_{i=0}^n\binom{n}{i}a^{n-i}b^i$。

应该长成这样吧,不太确定有没有问题。

所以又可以化简成下面那样。

$$=(n-1)(s+1)^{n-2}+(s+1)^{n-1}$$
$$=(s+1)^{n-2}(s+1+n-1)$$
$$=(s+1)^{n-2}(s+n)$$

然后就可以代回去了。

$$p=\sum\limits_{j=0}^{k-1}\dfrac{(-1)^j}{j!\times(k-j-1)!}[(n-1)(k-j)^{n-2}+(k-j)^{n-1}]$$
$$=\sum\limits_{j=0}^{k-1}\dfrac{(-1)^j}{j!\times(k-j-1)!}(k-j)^{n-2}(n+k-j-1)$$

所以就可以得出代码了。
我的时间复杂度比较劣,代码还是参考别人的吧。

时间复杂度 $\Theta(k \log \text{Mod})$。

不要问我为什么后面是 $\text{Mod}$ 而不是 $n$(
问就是逆元没有线性求,直接 qpow(x,mod-2)(((

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
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
int n, k, cnt, sum, a, jc[N];

void init() {
jc[0] = jc[1] = 1ll;
for (int i=2; i<=k; ++i)
jc[i] = jc[i-1] * i % MOD;
// 预处理阶乘
return ;
}

int qpow(int bas, int pw) {
int mult = 1ll;
while (pw) {
if (pw & 1ll) mult = mult * bas % MOD;
bas = bas * bas % MOD;
pw /= 2;
// 貌似是个 UB,不能写 >>=1ll
} return mult;
}

int inv(int x) { return qpow(x, MOD-2); }

signed main() {
// freopen("sum.in", "r", stdin);
// freopen("sum.out", "w", stdout);
scanf("%lld%lld", &n, &k); init();
for (int i=1; i<=n; ++i)
scanf("%lld", &a), cnt = (cnt + a) % MOD;
int Sum = 0;
for (int i=0; i<k; ++i) {
int cur = inv(jc[i]);
cur = cur * inv(jc[k-i-1]) % MOD;
int f = i&1? -1 : 1;
cur = f * cur % MOD, cur = (cur % MOD + MOD) % MOD;
cur = cur * qpow(k-i, n-2) % MOD;
cur = cur * (n+k-i-1) % MOD;
Sum = (Sum + cur) % MOD;
// 对于分母求逆元再乘分子,基本操作
}
cout << Sum * cnt % MOD << endl;
return 0;
}
P2150 Solution

P2150 Solution

Simplification for the Question

  • 有 $[2,n]$ 总共 $n-1$ 个数。
  • 从中挑选一些不重复的数字(可以不全选),组成集合 $S$ 和 $T$。
  • 求使得 $\forall x \in S,y \in T,\gcd(x,y)=1$ 的方案总数。

Part 1 - 30pts Algorithm

首先看到最简单的 30pts 算法,最暴力的部分总能给我们启示。

对于这 30pts,观察到 $n \le 30$。

那么这意味着什么呢?对,就是暴力。

首先看到 $\gcd$,求最大公约数,由唯一分解定理可知每一个数都可以由若干质数或质数的幂的连乘积构成,那么显然问题转化为:求使得 $\forall x\in S,y \in T$,设 $x$ 包含的质数集合为 $P$,设 $y$ 包含的质数集合为 $P’$,有 $P∩P’=\varnothing$,像这样的方案总数。

由于一个数的整数因数一定 $\le$ 它本身,所以说最大的质数一定 $\le n$,即 $p \le n \le 30$。
其中 $p$ 表示当前情况的质数集合。

很显然,经过枚举,我们发现 $30$ 以内有 $10$ 个质数(

所以直接暴力状压 DP,将质数的包含情况作为状态,包含为 $1$,否则为 $0$。

那么这个就是 30pts 的暴力算法了,时间复杂度 $\Theta(2^{2p}N)$,其中 $p$ 为质数个数。


Part 2 - ? pts Algorithm

我也不知道中间那两档分是用来干什么的。
所以直接看正解算了吧。/kel


Part 3 - 100pts Algorithm

首先我们判一个数是否是质数,最暴力的是这样判的。

  • 试除法

大概就长这样

1
2
3
4
5
function (int n)
if n:0 / n:1 -> back(0)
for i in range(2,sqrt(n))
if !(n mod i) -> back(0)
back(1)

那么看到这个 $\sqrt{n}$,这个就是解题的重点。

首先 $\sqrt{n} \times \sqrt{n}=n$。

接着,分两种情况讨论质数 $p$。

  • $p\ge\sqrt{n}$

那么设 $p \times k=n$,则显然 $k \le \sqrt{n}$。

又因为因数不大于这个数本身,所以 $\ge\sqrt{n}$ 的数最多只有两个,即 $>\sqrt{n}$ 的数最多只有一个。

  • $p<\sqrt{n}$

这个不用讲了吧。

那么又看到我们之前的暴力。

我们搞暴力,就是处理 $20$ 以内的质数。

但是现在我们发现 $>\sqrt{n}$ 的数最多只有一个,那么只要处理 $\le \sqrt{n}$ 的数,并且新开一个记录这个 $>\sqrt{n}$ 的数即可。

然后我们又看到 $n \le 500$。

而 $\sqrt{500}=22.360679775…$

所以说这个暴力就是个铺垫嘛,处理 $22$ 反而只有八个质数。

所以我们就想到了正解,也就是,同样枚举八个质数的情况,然后再根据大质数来判断是否可选即可。

但是我们怎么能保证大质数呢?

很显然,直接大质数从小到大结构体排个序即可。
然后对于两个人取的 DP 数,直接暴力处理然后合并即可。

注意合并的时候要容斥,因为统计会同时处理两个人都不选的情况,所以在合并的时候要减掉一个原来的数值。

恭喜你,你又 A 掉了一道黑题。

时间复杂度优化为恒定 $\Theta(2^{16}N)$

其中有一个地方要注意一下,就是在判断一开始预处理的质数情况时,贡献一定要加到对面去而不是自己的地方,因为如果加到自己的地方,无法保证对面和自己没有重复的元素,但是因为我们已经判断过这个质数和自己的关系,所以即使这个质数情况与对面有重复,加到一起仍然与自己合法,因为一定不会有重复的地方。

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
87
88
89
90
91
92
93
94
95
96
97
#include <bits/stdc++.h>
#define ri register int
using namespace std;

const int N = 5e2 + 10;
const int M = 1<<8;
const int pr[8] = {2,3,5,7,11,13,17,19};
int n, Mod, dp[M][M];
int Mer1[M][M], Mer2[M][M];
// Merge after dealing with, to DP

void read(int &ret) {
ret = 0; char ch = getchar();
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) {
ret = (ret<<1) + (ret<<3) + (ch^48);
ch = getchar();
}
return ;
}

struct Number {
int value;
int MaxiPrime;
int OtherPrime;
void dealwith() {
// deal with possible primes
int cur = value;
for (ri i=0; i<8; ++i) {
// choose the primes
if (cur % pr[i]) continue;
// can't be devided with no left
OtherPrime |= (1<<i);
while (!(cur % pr[i])) cur /= pr[i];
}
if (cur > 1) MaxiPrime = cur;
return ;
}

bool operator <(const Number &X) const {
return MaxiPrime < X.MaxiPrime;
}
} a[N];

void init() {
read(n), read(Mod);
for (ri i=2; i<=n; ++i)
a[i-1].value = i, a[i-1].dealwith();
sort(a+1, a+n), dp[0][0] = 1;
return ;
}

void Merge(ri &x, ri y) {
x = (x + y) % Mod;
return ;
}

int main() {
init();
// predict:
// Rolling Array, Back-to-Head Scan!
for (ri i=1; i<n; ++i) {
if (i == 1 || a[i].MaxiPrime ^ a[i-1].MaxiPrime || !a[i].MaxiPrime)
memcpy(Mer1, dp, sizeof Mer1),
memcpy(Mer2, dp, sizeof Mer2);
// just copy from dp to Merge

for (ri j=M-1; j>=0; --j)
for (ri k=M-1; k>=0; --k) {
// enumarate each possible situation
// which is of the two people
if (j&k) continue;
// has the same elements
if (!(j&a[i].OtherPrime))
Merge(Mer1[j][k|a[i].OtherPrime], Mer1[j][k]);
if (!(k&a[i].OtherPrime))
Merge(Mer2[j|a[i].OtherPrime][k], Mer2[j][k]);
# 需要注意的就是这里,不要写反了
}

if (i == n-1 || a[i].MaxiPrime ^ a[i+1].MaxiPrime || !a[i].MaxiPrime)
for (ri j=0; j<M; ++j)
for (ri k=0; k<M; ++k) {
if (j&k) continue;
// has the same elements
ri to = Mer1[j][k] + Mer2[j][k] - dp[j][k];
to %= Mod, dp[j][k] = to;
dp[j][k] = (dp[j][k] + Mod) % Mod;
}
}
ri ans = 0;
for (ri i=0; i<M; ++i)
for (ri j=0; j<M; ++j)
if (!(i&j)) Merge(ans, dp[i][j]);
cout << ans << endl;
return 0;
}
Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×