N个人围绕着圆桌坐着,其中一些是男孩,另一些是女孩。你的任务是找出所有合法的方案数,使得不超过K个女孩座位是连续的。方案数对1e8+7取模
循环同构会被认为是同一种方案。T组数据
对于20%的数据T<=20,N,K<=20;
对于100%的数据T<=50,N,K<=2000;
我们先不考虑循环同构。
一个圈好麻烦。我们可以先把第一个设为男生,因为整个圈除了没有男生的情况,都会有男生,那我们可以先把圈旋转过来。全部是女生的情况特判就好。
设F[i][j]为共有i个人,最后有连续j个女生,边界:F[1][0]=1。
转移明显:F[i][j]⇒F[i+1][j+1]与F[i+1][0]。
这样会漏掉第一个不是男生的方案,那么对于每一个F[i][j],都乘上(j+1)即可。很简单吧?但是还有循环同构····
怎么办呢?考虑怎样的序列会循环同构。我们可以发现,只有有循环节,设长度x,x|n的时候,才会算重。这样的话,我们可以枚举n的约数来处理。
设r[i]为大小为i的圈的方案数,g[i]为没有循环节的大小为i的圈。
则 g[i]=r[i]−∑j|i,j<ig[j]
然后 ans=∑i|ng[i]/i ,记得求i的逆元。
瞎打循环,慢的要死又没用···
#include
#include
#include
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
typedef long long ll;
const int N=2005,mo=100000007;
int f[N][N],T,i,j,n,m,k,inv[N],r[N],g[N];
ll ans;
ll ksm(ll x,ll y)
{
if (y==0) return 1;
if (y==1) return x;
ll dur=ksm(x,y/2);
return dur*dur%mo*ksm(x,y%2)%mo;
}
void predo()
{
fo(i,1,n)
fo(j,0,n) f[i][j]=0;
f[1][0]=1;
fo(i,1,n-1)
fo(j,0,min(i-1,m))
{
(f[i+1][j+1]+=f[i][j])%=mo;
(f[i+1][0]+=f[i][j])%=mo;
}
}
int main()
{
freopen("t2.in","r",stdin);
freopen("t2.out","w",stdout);
scanf("%d\n",&T);
fo(i,1,2000) inv[i]=ksm(i,mo-2);
while (T--)
{
scanf("%d%d",&n,&m);
ans=0;
predo();
fo(j,1,n)
{
r[j]=0;
fo(i,0,m)
r[j]=(r[j]+(ll)f[j][i]*i%mo+f[j][i])%mo;
}
fo(i,1,n)
{
g[i]=r[i];
fo(j,1,i-1)
if (i%j==0)
g[i]=(g[i]-g[j]+mo)%mo;
}
fo(i,1,n)
if (n%i==0)
ans=(ans+(ll)g[i]*inv[i]%mo)%mo;
if (n<=m) ans=(ans+1)%mo;
printf("%lld\n",ans);
}
}