题目:BZOJ1547.
题目大意:给定一个长度为
n
n
n的环,要求每个位置染成
0
0
0或
1
1
1,其中连续的
0
0
0数量不超过
m
m
m,求方案数.循环同构算一种方案但翻转同构不算.
数据组数
T
≤
50
T\leq 50
T≤50,
1
≤
n
,
m
≤
2000
1\leq n,m\leq 2000
1≤n,m≤2000,答案对
1
0
8
+
7
10^8+7
108+7取模.
首先看到了循环同构算一种方案就可以想到用Burnside引理转化成统计 n n n个置换中的每个循环染同色的方案数.
考虑对于一个转了 a a a次的循环,根据一些数论知识,我们知道总共会有 g c d ( n , a ) gcd(n,a) gcd(n,a)个循环,显然这些循环会形成一些长度为 n g c d ( n , a ) \frac{n}{gcd(n,a)} gcd(n,a)n的循环节,每个循环节内部的所有位置互不相干.
问题变成了如何统计一个长度为 g c d ( n , a ) gcd(n,a) gcd(n,a)的环中没有超过 m m m个连续的 0 0 0的染色方案.显然若 m + 1 ≥ g c d ( n , a ) m+1\geq gcd(n,a) m+1≥gcd(n,a)时答案就是 2 g c d ( n , a ) − [ m + 1 ≤ n ] 2^{gcd(n,a)}-[m+1\leq n] 2gcd(n,a)−[m+1≤n],现在考虑 m ≤ g c d ( n , a ) m\leq gcd(n,a) m≤gcd(n,a)的情况.
发现此时环并不好解决但是链好办,所以先统计链的方案数,再减去在链上没有超过 m m m个 0 0 0但首尾相连后就有的方案数即可.
为了实现这一点,考虑一个DP.设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示长度为
i
i
i的链上满足连续的
0
0
0不超过
m
m
m个且倒数第
j
+
1
j+1
j+1个染为
1
1
1最后
j
j
j个都染为
0
0
0的方案数,容易得到状态转移方程:
f
[
i
]
[
j
]
=
{
∑
k
=
0
m
f
[
i
−
1
]
[
k
]
j
=
0
f
[
i
−
1
]
[
j
−
1
]
j
>
0
f[i][j]= \left\{\begin{matrix} \sum_{k=0}^{m}f[i-1][k]&j=0\\ f[i-1][j-1]&j>0 \end{matrix}\right.
f[i][j]={∑k=0mf[i−1][k]f[i−1][j−1]j=0j>0
链的方案数显然为 ∑ i = 0 m f [ g c d ( n , a ) ] [ i ] \sum_{i=0}^{m}f[gcd(n,a)][i] ∑i=0mf[gcd(n,a)][i],然后考虑计算链上没有超过 m m m个连续的 0 0 0但首尾相连后有的方案数.
考虑枚举开头连续
0
0
0的数量
i
i
i,再枚举结尾连续
0
0
0的数量,就可以得到方案数为:
∑
i
=
1
min
(
g
c
d
(
n
,
a
)
−
1
,
m
)
∑
j
=
m
+
1
−
i
m
f
[
g
c
d
(
n
,
a
)
]
[
j
]
\sum_{i=1}^{\min(gcd(n,a)-1,m)}\sum_{j=m+1-i}^{m}f[gcd(n,a)][j]
i=1∑min(gcd(n,a)−1,m)j=m+1−i∑mf[gcd(n,a)][j]
前缀和一下就可以省去一个 ∑ \sum ∑,时间复杂度降为 O ( m ) O(m) O(m).
总时间复杂度 O ( T n m ) O(Tnm) O(Tnm).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=2000,mod=100000007;
int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
int sub(int a,int b){return a-b<0?a-b+mod:a-b;}
int mul(int a,int b){return (LL)a*b%mod;}
void sadd(int &a,int b){a=add(a,b);}
void ssub(int &a,int b){a=sub(a,b);}
void smul(int &a,int b){a=mul(a,b);}
int Power(int a,int k){int res=1;for (;k;k>>=1,smul(a,a)) if (k&1) smul(res,a);return res;}
int Get_inv(int a){return Power(a,mod-2);}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int n,m;
int dp[N+9][N+9];
void Get_dp(){
dp[0][0]=1;
for (int i=1;i<=n;++i){
dp[i][0]=dp[i][m+1]=0;
for (int j=0;j<=m;++j){
sadd(dp[i][0],dp[i-1][j]);
if (j>0) dp[i][j]=dp[i-1][j-1];
}
}
for (int i=1;i<=n;++i)
for (int j=m;j>=0;--j) sadd(dp[i][j],dp[i][j+1]);
}
int Solve(int len){
if (len<=m+1) return sub(Power(2,len),m+1<=n);
int res=dp[len][0];
for (int i=1;i<len&&i<=m;++i) ssub(res,dp[len-i-1][m+1-i]);
return res;
}
int ans;
Abigail into(){
scanf("%d%d",&n,&m);
}
Abigail work(){
Get_dp();
ans=0;
for (int i=0;i<n;++i) sadd(ans,Solve(gcd(n,i)));
}
Abigail outo(){
printf("%d\n",mul(ans,Get_inv(n)));
}
int main(){
int T;
scanf("%d",&T);
while (T--){
into();
work();
outo();
}
return 0;
}