Irena和Sirup正准备下个周末的Party。为这个Party,他们刚刚买了一个非常大的圆桌。他们想邀请每个人,但他们现在不知道如何分配座次。Irena说当有超过K个女孩座位相邻(即这些女孩的座位是连续的,中间没有男孩)的话,她们就会说一整晚的话而不和其他人聊天。
Sirup没有其他选择,只有同意她。然而,作为一名数学家,他很快地痴迷于所有可能方案。
题目说明:
N个人围绕着圆桌坐着,其中一些是男孩,另一些是女孩。你的任务是找出所有合法的方案数,使得不超过K个女孩座位是连续的。
循环同构会被认为是同一种方案。
我们回忆Burnside定理:
假设第i种置换下合法不动点数量为g(i)
在n中置换的约束下,不同方案数为 ∑ni=1g(i)n
因此我们想要计算g。
这题也有n种置换,第i种就是左移i格
我们考虑如何求g[i],首先因为左移i格不动,所以a[0]=a[i]=a[i*2]=……
什么时候会转回来呢?也就是从0开始走什么时候会回到0?
ix≡0(modn)
x≡0(modn(n,i))
所以 x=n(n,i)
于是我们知道一共有(n,i)个这样的环,出发点是0,1,2……(n,i)-1
我们现在思考,一个合法的第i种置换下不动点,不能有连续的超过k个1,首尾相接也不能有,那是什么情况呢?
首先,我们先排除k=n的情况,因为此时 g[i]=2(n,i)
然后,如果(n,i)<=k,那么只有全是1的情况才非法,因此 g[i]=2(n,i)−1
否则的话,假如整个序列一个循环节中出现连续超过k个1,那么第一个循环节内就出现连续超过k个1。如果两个循环节交交界处出现连续超过k个1,那么第一个循环节首尾相接就会出现连续超过k个1。
所以,只需要考虑一个循环节即可!
我们先预处理f[i]表示长度为i的序列,填0/1,不能有连续的超过k个1,有多少种方案。
那么 f[i]=f[i−1]∗2−f[i−k−2]
其中f初值是f[-1]=f[0]=1,其余f都为0
这个方程不难理解,在长度为i-1的基础上,末尾添0/1。
此时,非法的情况只有末尾出现k+1个1,然后这k+1个1最前有一个0(必须有这个0,因为f[i-1]说明了不添最后一个1只有k个1,因为f[i-1]合法,所以最前面必须是0,当然,也有只有k+1个1的情况,最前面没有任何东西,所以还有f[-1]=1)
求出f之后,怎么求g?
我们当然可以先让g[i]=f[(n,i)],但是会算多。因为f并没有考虑首尾相接的情况。
于是我们尝试减去首尾相接会超的情况。
枚举右端连续1的个数为j,1<=j<=k,显然这j个1前还有有一个0
那么左端至少有k-j+1个连续1,最多能有多少个呢?最多只能有k个。
但是还有注意,现在 k<n ,我们不能让左右端加起来超过(n,i),所以左端至多只能有(n,i)-k-2个
右端下限确定了,我们来枚举增量l。
∑min(j−1,(n,i)−k−2)l=0f[(n,i)−k−l−3]
嘿嘿,右边那个玩意与j无关,因此可以预处理一下前缀和。
然后再枚举j,加上去即可。
不懂看一下代码
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=2000+10,mo=100000007;
int len[maxn],g[maxn],f[maxn],two[maxn],num[maxn];
int i,j,k,l,r,t,n,m,ca,ans,ni;
int gcd(int a,int b){
return b?gcd(b,a%b):a;
}
int quicksortmi(int x,int y){
if (!y) return 1;
int t=quicksortmi(x,y/2);
t=(ll)t*t%mo;
if (y%2) t=(ll)t*x%mo;
return t;
}
void getnum(int n){
int l;
if (n-k-3<0) num[0]=1;
else num[0]=f[n-k-3];
fo(l,1,min(k-1,n-k-2)){
num[l]=num[l-1];
if (n-k-l-3<0) num[l]++;
else (num[l]+=f[n-k-l-3])%=mo;
}
}
int main(){
freopen("t22.in","r",stdin);freopen("t2.ans","w",stdout);
scanf("%d",&ca);
while (ca--){
scanf("%d%d",&n,&k);
fo(i,1,n) len[i]=gcd(n,i);
f[0]=1;
fo(i,1,n){
f[i]=(ll)f[i-1]*2%mo;
if (i==k+1) f[i]--;
else if (i>k+1) (f[i]-=f[i-k-2])%=mo;
}
if (k>=n){
fo(i,1,n){
t=len[i];
g[i]=f[t];
}
ni=quicksortmi(n,mo-2);
ans=0;
fo(i,1,n) (ans+=g[i])%=mo;
ans=(ll)ans*ni%mo;
(ans+=mo)%=mo;
printf("%d\n",ans);
continue;
}
else if (k==1){
fo(i,1,n){
t=len[i];
if (t==1) g[i]=1;
else{
g[i]=f[t];
if (t==3) g[i]--;
else g[i]-=f[t-4];
}
}
ni=quicksortmi(n,mo-2);
ans=0;
fo(i,1,n) (ans+=g[i])%=mo;
ans=(ll)ans*ni%mo;
(ans+=mo)%=mo;
printf("%d\n",ans);
continue;
}
fo(i,1,n){
t=len[i];
if (t<=k) g[i]=f[t]-1;
else if (t==k+1) g[i]=f[t];
else{
getnum(t);
r=0;
fo(j,1,k) (r+=num[min(j-1,t-k-2)])%=mo;
g[i]=(f[t]-r)%mo;
}
}
ni=quicksortmi(n,mo-2);
ans=0;
fo(i,1,n) (ans+=g[i])%=mo;
ans=(ll)ans*ni%mo;
(ans+=mo)%=mo;
printf("%d\n",ans);
}
}