B君的宴请

题目描述

这里写图片描述

旋转

考虑只旋转。
如果旋转i下,容易知道形成(n,i)个环,每个环大小为n/(n,i),而我们需要k个,因此需要选k(n,i)/n个,在(n,i)个里,且必须互不相邻(包括首尾),这个可以考虑组合数算。
一般化,长度为n的环选出k个不相邻,考虑把n-k个数塞进k个数里,两两间必须塞至少一个,然后讨论最前至少塞1个最后不塞最前不塞最后至少塞1个以及最前最后都至少塞一个,鸽笼原理组合数即可。

翻转

同时有翻转和旋转时,其实可以不理会旋转,然后就是任做一条对称轴。
根据n、k的奇偶性进行讨论,比较容易,详见代码。

#include<cstdio>
#include<algorithm>
#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=1000000+10,mo=1000000007;
int fac[maxn],inv[maxn];
int i,j,k,l,t,n,m,ans;
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;
}
int gcd(int a,int b){
    return (b?gcd(b,a%b):a);
}
int C(int n,int m){
    if (m<0||n<m) return 0;
    return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;
}
int solve(int n,int m){
    return (2*C(n-m-1,m-1)%mo+C(n-m-1,m))%mo;
}
int calc(int n,int m){
    if (n<0){
        if (m==0) return 1;else return 0;
    }
    if (m<0) return 0;
    if (n==0){
        if (m==0) return 1;else return 0;
    }
    if (n==m){
        if (n==1) return 1;else return 0;
    }
    if (n-m-1<0) return 0;
    int t=C(n-m-1,m-2);
    t=(t+2*C(n-m-1,m-1)%mo)%mo;
    t=(t+C(n-m-1,m))%mo;
    return t;
}
int main(){
    freopen("round.in","r",stdin);freopen("round.out","w",stdout);
    scanf("%d%d",&n,&k);
    if (n==1){
        if (k==0) printf("1\n");else printf("0\n");
        return 0;
    }
    if (n==2){
        if (k==0||k==1) printf("1\n");else printf("0\n");
        return 0;
    }
    if (n==4){
        if (k==0||k==1||k==2) printf("1\n");else printf("0\n");
        return 0;
    }
    fac[0]=1;
    fo(i,1,n) fac[i]=(ll)fac[i-1]*i%mo;
    inv[n]=quicksortmi(fac[n],mo-2);
    fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo;
    ans=solve(n,k);
    fo(i,1,n-1){
        t=gcd(n,i);
        if ((ll)k*t%n==0){
            (ans+=solve(t,(ll)k*t/n))%=mo;
        }
    }
    if (n%2==1){
        if (k%2==1) ans=(ans+(ll)calc((n-1)/2-2,(k-1)/2)*n%mo)%mo;
        else ans=(ans+(ll)calc((n-1)/2-1,k/2)*n%mo)%mo;
    }
    else{
        if (k%2==1) ans=(ans+(ll)2*calc(n/2-2,(k-1)/2)%mo*(n/2)%mo)%mo;
        else{
            ans=(ans+(ll)calc(n/2-2,k/2)*(n/2)%mo)%mo;
            ans=(ans+(ll)calc(n/2-3,k/2-1)*(n/2)%mo)%mo;
            ans=(ans+(ll)calc(n/2-1,k/2)*(n/2)%mo)%mo;
        }
    }
    ans=(ll)ans*quicksortmi(2*n,mo-2)%mo;
    (ans+=mo)%=mo;
    printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值