求1~n的排列中,i不放在i和i+1且n不放在1的方案数。
n<=10^5
容斥原理乱搞。
表示蒟蒻只能看题解了。。。
我们设W(k)表示至少有k个位置不合法的方案数。
那么,我们把每个位置要选的按顺序写下来,得到了1,2,2,3,3,4…..n-1,n,n,1这个东西。
那么我们要求的方案数就是在这个数列里选择k个不同的数的方案数。
这个东西相当于在n个端点的圆环上选出k个不相邻的节点的方案数。
然后我们再次求一个子问题,一个n个数的数列上选出k个不相邻的节点的方案数。
这个东西等价于把n-k个球放在0~k这些盒子里,且1~k-1这些格子里必须有数。
那么我们加上两个球,相当于把n-k+2个求放在k+1个盒子里,且每个盒子里必须有球。
为 Ckn−k+1
现在我们回到原问题,首先,选择环上的一个点选择,将环破开成链,然后用这个公式。
为 Ck−1(n−3)−(k−1)+1=Ck−1n−k−1
然后,因为每个点都可以选,乘上n,然后每种方案都会被算k次,除掉k(用逆元)
所以方案数就是 Ck−1n−k−1nk
那么W(k)= Ck−12n−k−12nk(n−k)!
然后就直接容斥就好了。
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 200005
using namespace std;
typedef long long ll;
const int mo=1000000000+7;
int n,ans,z,f[N],g[N],ni[N];
int c(int m,int n) {
int cnt=(ll)f[m]*g[n]%mo;
cnt=(ll)cnt*g[m-n]%mo;
return cnt;
}
int mi(int x,int y) {
int z=x;
for(y--;y;y/=2,x=(ll)x*x%mo) if (y&1) z=(ll)z*x%mo;
return z;
}
int main() {
f[0]=g[0]=ni[0]=1;
fo(i,1,N-5) f[i]=(ll)f[i-1]*i%mo,g[i]=mi(f[i],mo-2),ni[i]=mi(i,mo-2);
while(scanf("%d",&n)!=EOF) {
if (n==1) {printf("0\n");continue;}
ans=f[n];
fo(i,1,n) {
int sum=(ll)2*n*f[n-i]%mo;
sum=(ll)sum*c(2*n-i-1,i-1)%mo;
sum=(ll)sum*ni[i]%mo;
if (i&1) ans=(ans-sum+mo)%mo;
else ans=(ans+sum)%mo;
}
printf("%d\n",ans);
}
}
Ps:其实这道题是有递推公式的: