【NOIP2013模拟联考6】选课(select)

Description

求1~n的排列中,i不放在i和i+1且n不放在1的方案数。
n<=10^5

Solution

容斥原理乱搞。
表示蒟蒻只能看题解了。。。
我们设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个盒子里,且每个盒子里必须有球。
Cknk+1
现在我们回到原问题,首先,选择环上的一个点选择,将环破开成链,然后用这个公式。
Ck1(n3)(k1)+1=Ck1nk1
然后,因为每个点都可以选,乘上n,然后每种方案都会被算k次,除掉k(用逆元)
所以方案数就是 Ck1nk1nk
那么W(k)= Ck12nk12nk(nk)!
然后就直接容斥就好了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#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:其实这道题是有递推公式的:

Fi=F[i1]i+(F[i2]+4(1)imod2+1)/(i2)

不要问为什么!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值