Description
求n个点的圆排列,每个点是1或0,并且连续的0不超过k个的方案数。
循环同构算一种。多组数据。
T<=50,n,k<=2000
Solution
首先,先不考虑环的情况,我们来处理一下连续的k个。
一个很显然的想法是,Fi,j表示,前i个数,第1个是1且后面j个是0的方案数。
看一下这一位放0还是1就可以推出转移方程了。
如果是在圆上呢?
那么我们设ri表示长度为i的圆的方案数。
因为我们保证了第一个是1,所以枚举圆方案的两边加起来为j,那么就有j+1种方法。
于是
r[i]=∑j=0kF[i][j]∗(j+1)
然后,我们考虑循环同构。
设gi表示长度为i的没有循环节的方法。
然后很显然可以用容斥解决。
枚举i的循环节长度,然后把对应的方案减去。
g[i]=r[i]−∑j|i,j<ig[j]
所以答案就是
∑i|ng[i]i
因为考虑循环节就是i,所以除掉。
还有,如果k>=n,答案要+1(毕竟无法处理全是0的情况)
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 2005
using namespace std;
typedef long long ll;
const int mo=1e8+7;
ll f[N][N],r[N],g[N],ans;
int n,k,ty;
ll mi(ll x,int y) {
ll z=1;
for(;y;y/=2,x=x*x%mo) if (y&1) z=z*x%mo;
return z;
}
int main() {
for(scanf("%d",&ty);ty;ty--) {
scanf("%d%d",&n,&k);ans=0;
if (k>=n) ans=1;
if (ty==34) {
ans=ans;
k=k;
}
// 1
fo(i,1,n) f[i][0]=0;f[1][0]=1;
fo(i,1,n-1) fo(j,0,k) f[i+1][j+1]=f[i][j],f[i+1][0]=(f[i+1][0]+f[i][j])%mo;
// 2
fo(i,1,n) {
r[i]=0;
fo(j,0,k) r[i]=(r[i]+(ll)f[i][j]*(j+1)%mo)%mo;
g[i]=r[i];
}
fo(i,1,n) fo(j,1,i-1) if (!(i%j)) g[i]=(g[i]-g[j]+mo)%mo;
fo(i,1,n) if (!(n%i)) ans=(ans+g[i]*mi(i,mo-2)%mo)%mo;
printf("%lld\n",ans);
}
}