题意:求满足下面条件的n的排列个数:条件为给出m个点对<x,y> 只要这个排列满足至少一个位置p[x]=y即算满足条件。
解法:这道题不算难应该要想出来的,结果没想出来自己还是蒟蒻呀qwq。我们观察满足一个p[x]=y即可以,但是这样很难统计,发现它的反面条件就是不满足任何一个p[x]=y。于是我们从这个入手用状压DP求出不满足条件数量然后用总数n!减去即是答案。
设dp[i][state]代表长度为i的排列使用数字情况state的不完美数量
用填表法DP,写出状态转移方程 dp[i+1][state|(1<<k)]+=dp[i][state] (如果state状态下i位置能填数字k)。 然后照着这个DP方程写就可以了。
#include<bits/stdc++.h> using namespace std; typedef long long LL; int n,m,g[20][20]; LL fac[20],dp[1<<18]; //dp[i][state]代表长度为i的排列使用数字情况state的不完美数量 int main() { int T,Case=0; cin>>T; fac[0]=1; for (int i=1;i<=17;i++) fac[i]=fac[i-1]*i; while (T--) { scanf("%d%d",&n,&m); int ALL=(1<<n)-1; memset(g,0,sizeof(g)); for (int i=1;i<=m;i++) { int x,y; scanf("%d%d",&x,&y); g[x][y]=1; //x位置不能y } memset(dp,0,sizeof(dp)); dp[0]=1; LL sum=0; for (int i=1;i<=n;i++) { //长度i:现在填到位置i for (int j=ALL;j>=0;j--) { //数字使用情况state for (int k=1;k<=n;k++) { //这一位置填数字k if ((1<<(k-1))&j) continue; if (g[i][k]) continue; dp[(1<<(k-1))|j]+=dp[j]; } dp[j]=0; } } for (int i=0;i<=ALL;i++) sum+=dp[i]; printf("Case %d: %lld ",++Case,fac[n]-sum); } return 0; }