正题
题目链接:https://ac.nowcoder.com/acm/contest/7745/C
题目大意
求一nnn的排列,给mmm个限制pip_ipi表示1∼pi1\sim p_i1∼pi不能是pip_ipi的排列。求方案数。
解题思路
定义fif_ifi表示1∼pi1\sim p_i1∼pi是pip_ipi的排列的情况下1∼pi1\sim p_i1∼pi的方案数,显然有fi=pi!f_i=p_i!fi=pi!。但是如果我们用这个计算就会重复,所以我们需要容斥。定义fif_ifi表示1∼pi1\sim p_i1∼pi是pip_ipi的排列,且1∼pk1\sim p_k1∼pk不是pkp_kpk的排列(k<i)(k<i)(k<i)的情况下1∼pi1\sim p_i1∼pi的方案数,那么有转移方程fi=pi!−∑j=1i−1fj∗(pi−pj)!f_i=p_i!-\sum_{j=1}^{i-1}f_{j}*(p_i-p_j)!fi=pi!−j=1∑i−1fj∗(pi−pj)!
然后答案ans=n!−∑i=1mfi∗(n−pi)!ans=n!-\sum_{i=1}^mf_i*(n-p_i)!ans=n!−i=1∑mfi∗(n−pi)!
时间复杂度O(m2)O(m^2)O(m2)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=2100,XJQ=20000311;
ll n,m,fac[N],p[N],f[N];
int main()
{scanf("%lld%lld",&n,&m);fac[0]=1;for(ll i=1;i<=n;i++)fac[i]=fac[i-1]*i%XJQ;for(ll i=1;i<=m;i++)scanf("%lld",&p[i]);sort(p+1,p+1+m);for(ll i=1;i<=m;i++){f[i]=fac[p[i]]%XJQ;for(ll j=1;j<i;j++)f[i]=(f[i]-f[j]*fac[p[i]-p[j]]%XJQ+XJQ)%XJQ;}ll ans=fac[n];for(ll i=1;i<=m;i++)ans=(ans-f[i]*fac[n-p[i]]%XJQ+XJQ)%XJQ;printf("%lld",ans);
}