4714: 旋转排列 容斥原理

本文介绍了一种使用容斥原理解决特定排列组合问题的方法。通过构建图模型,问题被转换为计算不同长度环的个数。文章详细解释了算法步骤,并提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题解:

考试时脑子不清醒啊,搞了很久……其实是个普通的容斥。问题可以转化为,若每个点ii都向pi连边,一种方案的贡献就是这个图中不同长度的环的个数。那么我们可以枚举环的长度ii,然后再算至少有j个环的方案,容斥即可。具体的算法如下:比如至少有33个长度为2的环的方案数,一共有nn个点,可以这样算:Cn2×Cn22×Cn42×fac[21]3×f[n6]÷fac[3]fac[i]fac[i]表示i!i!f[i]f[i]表示ii个数错排的方案数,其中前三个组合数的乘积表示选点的方案数,中间的阶乘的乘方是每个环中不同方法的方案数,因为是环,要固定一个起点,所以是点数-1的阶乘,错排是剩下的点合法方案的数量,最后除的阶乘是因为不考虑环与环之间的顺序。

代码:

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=500010;
const int mod=1000000007;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
int n,f[Maxn],fac[Maxn],fin[Maxn],inv[Maxn];
void pre()
{
    fac[0]=1;
    for(int i=1;i<=n;i++)fac[i]=(LL)fac[i-1]*i%mod;
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)inv[i]=(LL)(mod-mod/i)*inv[mod%i]%mod;
    fin[0]=1;
    for(int i=1;i<=n;i++)fin[i]=(LL)fin[i-1]*inv[i]%mod;
    f[0]=1,f[1]=0,f[2]=1;
    for(int i=3;i<=n;i++)f[i]=(LL)(i-1)*((f[i-1]+f[i-2])%mod)%mod;
}
int C(int n,int m)
{
    if(n<m)return 0;
    return (LL)fac[n]*fin[m]%mod*fin[n-m]%mod;
}
int main()
{
    n=read();
    pre();
    int ans=0;
    for(int i=2;i<=n;i++)
    {
        int t=0,x=1,mx=n/i;
        for(int j=1;i*j<=n;j++)
        {
            int rest=n-i*j;
            x=(LL)x*C(n-(j-1)*i,i)%mod*fac[i-1]%mod;
            if(j&1)t=(LL)(t+(LL)fin[j]*x%mod*f[rest]%mod)%mod;
            else t=(LL)(t-(LL)fin[j]*x%mod*f[rest]%mod+mod)%mod;
        }
        ans=(ans+t)%mod;
    }
    printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值