题解:P9896 [ICPC 2018 Qingdao R] Sub-cycle Graph
非常好组合数学,使我推可爱的大柿子,而不是什么看起来就令人畏惧的生成函数。
抢到最优解了,于是考虑写个题解。
考虑一个“半环图”是什么样子的,不难发现其实就是一堆链和单点。于是发现我们可以按照度数把点分类,即度数为
设枚举的
首先,当然是要从
其次,我们考虑剩余的点。不难发现所有点一定构成
接下来,考虑将这些度数为
最后,我们需要将剩余度数为
于是根据乘法原理相乘即可得到结果为:
其中,
但是,我们其实可以注意到,上式如果化简,则可以消去大量冗余的阶乘。最终化简后的形式实际上如下:
发现好算了不少,于是可以求得飞快。
给出代码:
#include<bits/stdc++.h>
#define R register
using namespace std;
typedef long long ll;
const int N=1e5+20,mod=1e9+7;
int jc[N],inv[N],inv2[N];
inline int pwr(int a,int b){
int ans=1;
for(;b;b>>=1){
if(b&1)ans=(ll)ans*a%mod;
a=(ll)a*a%mod;
}
return ans;
}
inline int calc(int i,int j,int k){
int ans=1;
ans=1ll*ans*jc[j-1]%mod;
ans=1ll*ans*inv[k]%mod;
ans=1ll*ans*inv[i-j-k]%mod;
ans=1ll*ans*inv2[i-j-k]%mod;
ans=1ll*ans*inv[2*j+k-i]%mod;
ans=1ll*ans*inv[i-j-k-1]%mod;
return ans;
}
int main()
{
int T;
scanf("%d",&T);
int mx=N-10;
jc[0]=1;
for(R int i=1;i<=mx;i++)jc[i]=(ll)i*jc[i-1]%mod;
inv[mx]=pwr(jc[mx],mod-2);
inv2[mx]=pwr(pwr(2,mx),mod-2);
for(R int i=mx-1;i>=0;i--){
inv[i]=(ll)inv[i+1]*(i+1)%mod;
inv2[i]=(ll)inv2[i+1]*2ll%mod;
}
while(T--){
int x,y;
scanf("%d%d",&x,&y);
if(x==y){
printf("%d\n",(ll)jc[x-1]*inv2[1]%mod);
continue;
}
if(y>x){
printf("%d\n",0);
continue;
}
if(y==0){
printf("%d\n",1);
continue;
}
int ans=0;
for(int z=max(0,x-2*y);z<=x-y-1;z++){
ans=(ans+calc(x,y,z))%mod;
}
ans=(ll)ans*jc[x]%mod;
printf("%d\n",ans);
}
return 0;
}