题意简述
一个环上有 N 个位置,每个位置要么是男要么是女,规定不能有超过连续 K 个女孩相邻。问本质不同的合法环有多少。循环同构被认为是相同。环中必须至少有一个男的。
1 <= K <= N <= 1e5
题解
这道题其实是比较经典的一种 Burnside 应用题。
根据 Burnside 定理:
a n s = 1 ∣ G ∣ ∑ g ∈ G λ ( g ) ans=\frac{1}{|G|}\sum_{g∈G}λ(g) ans=∣G∣1g∈G∑λ(g)
其中 λ ( g ) λ(g) λ(g) 是置换 g g g 的不动点个数,换句话说,是只考虑置换 g g g 时,变换后本质不变的方案数。这道题的 g g g 即旋转的位数,如果向右旋转 i i i 位,整个环本质不变的话,就相当于相邻的长度为 i i i 的段相同,然而环的总长为 n n n ,所以相当于相邻长度为 g c d ( n , i ) gcd(n,i) gcd(n,i) 的段相同。
然后我们再尝试求不考虑循环同构的情况下,长度为 1~n 的环的方案数。有 K 的限制,我们可以 DP 做。官方题解给了个极好的 DP :
令 dp[i] 表示有 i 个人,且最后一个人是男的方案总数。
那么一个递推式是:
d p [ i ] = d p [ i − 1 ] + d p [ i − 2 ] + . . . + d p [ i − k − 1 ] dp[i] = dp[i-1] + dp[i-2] + ... + dp[i-k-1] dp[i]=dp[i−1]+dp[i−2]+...+dp[i−k−1]
这个 dp 是可以通过前缀和加速的。
这样就可以先固定最后一个是男的,然后考虑前面,
假设一开始有连续y个女的,且第y+1个人是男的,那么前y个人是可以任意旋转的(共y+1种可能),对答案的贡献为 ( y + 1 ) ∗ d p [ i − y − 1 ] (y+1)*dp[i-y-1] (y+1)∗dp[i−y−1](通过这个公式应该可以理解上面那句话)。
这个是 O ( n 2 ) O(n^2) O(n2) 的,还可以优化:
对于第i种置换,有用的数仅仅是 gcd(i,n) 种,也就是说我们只需处理 n 的所有约数就可以了。
时间复杂度为 tot*n 。(其中tot表示n的约数个数)
直白的说,复杂度为 O ( n n ) O(n\sqrt n) O(nn) ,大多数人应该都这么做的吧。
这里有个 O ( 5 n ) O(5n) O(5n) 的解法不知当讲不当讲?😉
考虑上面的 DP 告诉我们不考虑循环同构的情况下,长度为 i 的环的方案数是
a n s [ i ] = ∑ y = 0 k d p [ i − y − 1 ] ∗ ( y + 1 ) ans[i]=\sum_{y=0}^k dp[i-y-1]*(y+1) ans[i]=y=0∑kdp[i−y−1]∗(y+1)
假设我们求出这个值了,怎么过渡到 i + 1 i+1 i+1 呢?
我们推一下式子:
a
n
s
[
i
+
1
]
=
∑
y
=
0
k
d
p
[
i
−
y
]
∗
(
y
+
1
)
ans[i+1]=\sum_{y=0}^k dp[i-y]*(y+1)
ans[i+1]=∑y=0kdp[i−y]∗(y+1)
=
∑
y
=
0
k
−
1
d
p
[
i
−
y
−
1
]
∗
(
y
+
2
)
+
d
p
[
i
]
~~~~~~~~~~~~~~~~~~=\sum_{y=0}^{k-1} dp[i-y-1]*(y+2)+dp[i]
=∑y=0k−1dp[i−y−1]∗(y+2)+dp[i]
=
∑
y
=
0
k
−
1
d
p
[
i
−
y
−
1
]
∗
(
y
+
1
)
+
∑
y
=
0
k
−
1
d
p
[
i
−
y
−
1
]
+
d
p
[
i
]
~~~~~~~~~~~~~~~~~~=\sum_{y=0}^{k-1} dp[i-y-1]*(y+1)+\sum_{y=0}^{k-1}dp[i-y-1]+dp[i]
=∑y=0k−1dp[i−y−1]∗(y+1)+∑y=0k−1dp[i−y−1]+dp[i]
=
∑
y
=
0
k
−
1
d
p
[
i
−
y
−
1
]
∗
(
y
+
1
)
+
∑
y
=
0
k
d
p
[
i
−
y
]
~~~~~~~~~~~~~~~~~~=\sum_{y=0}^{k-1} dp[i-y-1]*(y+1)+\sum_{y=0}^{k}dp[i-y]
=∑y=0k−1dp[i−y−1]∗(y+1)+∑y=0kdp[i−y]
=
∑
y
=
0
k
d
p
[
i
−
y
−
1
]
∗
(
y
+
1
)
−
d
p
[
i
−
k
−
1
]
∗
(
k
+
1
)
+
∑
y
=
0
k
d
p
[
i
−
y
]
~~~~~~~~~~~~~~~~~~=\sum_{y=0}^{k} dp[i-y-1]*(y+1)-dp[i-k-1]*(k+1)+\sum_{y=0}^{k}dp[i-y]
=∑y=0kdp[i−y−1]∗(y+1)−dp[i−k−1]∗(k+1)+∑y=0kdp[i−y]
=
a
n
s
[
i
]
−
d
p
[
i
−
k
−
1
]
∗
(
k
+
1
)
+
∑
y
=
0
k
d
p
[
i
−
y
]
~~~~~~~~~~~~~~~~~~=ans[i]-dp[i-k-1]*(k+1)+\sum_{y=0}^{k}dp[i-y]
=ans[i]−dp[i−k−1]∗(k+1)+∑y=0kdp[i−y]
其中 ∑ y = 0 k d p [ i − y ] \sum_{y=0}^kdp[i-y] ∑y=0kdp[i−y] 可以前缀和优化,于是我们就推出了 O ( n ) O(n) O(n) 求 a n s [ . . . ] ans[...] ans[...] 的方法。
最后求 1 n ∑ i = 1 n a n s [ g c d ( n , i ) ] \frac1n\sum_{i=1}^nans[gcd(n,i)] n1∑i=1nans[gcd(n,i)] 只需要每个求个 gcd ,gcd 的复杂度是 O ( lg m i n ( i , n ) ) O(\lg min(i,n)) O(lgmin(i,n)) 的,因此总复杂度为 O ( n lg n ) O(n\lg n) O(nlgn) 即最大为 O ( 5 n ) O(5n) O(5n) 。
CODE
#include<set>
#include<map>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define DB double
#define LL long long
#define ENDL putchar('\n')
#define SI set<cp>::iterator
#define lowbit(x) (-(x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
int dp[MAXN],sum_[MAXN];
int inv[MAXN],as[MAXN];
int gcd(int a,int b) {return b==0 ? a:gcd(b,a%b);}
int main() {
freopen("girls.in","r",stdin);
freopen("girls.out","w",stdout);
n = read();k = read();
dp[0] = 1;sum_[0] = 0;
inv[0] = inv[1] = 1;
for(int i = 1;i <= n;i ++) {
sum_[i] = (sum_[i-1] + dp[i-1]) % MOD;
dp[i] = (sum_[i] +MOD- sum_[max(0,i-k-1)]) % MOD;
if(i-1) inv[i] = (MOD-inv[MOD%i]) *1ll* (MOD/i) % MOD;
}
int sm1 = 0,sm2 = 1,sm3 = 0;
for(int i = 1;i <= n;i ++) {
(sm1 += sm2) %= MOD;
as[i] = sm1;
(sm2 += dp[i]) %= MOD;
if(i >= k+1) (sm2 += MOD-dp[i-k-1]) %= MOD,(sm1 += MOD-(dp[i-k-1] *1ll* (k+1) % MOD)) %= MOD;
(sm3 += as[gcd(i,n)]) %= MOD;
}
sm3 = sm3 *1ll* inv[n] % MOD;
printf("%d\n",sm3);
return 0;
}