Description
现在有n个人要排成一列,编号为1->n 。但由于一些不明原因的关系,人与人之间可能存在一些矛盾关系,具体有m条矛盾关系(u,v),表示编号为u的人想要排在编号为v的人前面。要使得队伍和谐,最多不能违背k条矛盾关系(即不能有超过k条矛盾关系(u,v),满足最后v排在了u前面)。问有多少合法的排列。答案对10^9+7取模。
Input
输入文件名为count.in。
第一行包括三个整数n,m,k。
接下来m行,每行两个整数u,v,描述一个矛盾关系(u,v)。
保证不存在两对矛盾关系(u,v),(x,y),使得u=x且v=y 。
Output
输出文件名为count.out。
输出包括一行表示合法的排列数。
Sample Input
输入1: 4 2 1 1 3 4 2 输入2: 10 12 3 2 6 6 10 1 7 4 1 6 1 2 4 7 6 1 4 10 4 10 9 5 9 8 10
Sample Output
输出1: 18 输出2: 123120
Data Constraint
对于30%的数据,n<=10
对于60%的数据,n<=15
对应100%的数据,n,k<=20,m<=n*(n-1),保证矛盾关系不重复。
n<=20,显然要状压DP,将排队的状态压成一个数来表示
对于一个队伍状态S,令F[S][k]表示S状态违反了k条矛盾的合法方案数,则有
F[S|2i-1][k+num(S&power[i])]=F[S|2i-1][k+num(S&power[i])]+F[S][k]
其中i表示某个不在队伍的人,num(i)表示i在二进制下1的个数,power[i]表示必须排在i后面的人的情况。
(实际操作中发现power[i]储存排在i后面的人的情况的时候运行速度远不及power[i]储存排在i前面的情况)
1 #include<cstdio> 2 using namespace std; 3 const int qaq=1000000007; 4 int f[1<<20][21],power[21],n,m,t; 5 int main(){ 6 freopen("count.in","r",stdin); 7 freopen("count.out","w",stdout); 8 scanf("%d%d%d",&n,&m,&t); 9 int u,v; 10 for (int i=1;i<=m;i++){ 11 scanf("%d%d",&u,&v); 12 power[u]|=1<<(v-1); //power[v]|=1<<(u-1); 13 } 14 f[0][0]=1; 15 for (int i=0;i<(1<<n);i++) 16 for (int j=0;j<=t;j++) 17 if (f[i][j]) 18 for (int k=1;k<=n;k++) 19 if ((i&(1<<(k-1)))==0){ 20 int qwq=power[k]&i; //int qwq=power[k]^(power[k]&i); 21 int sum=0; 22 while (qwq){ 23 sum++; 24 qwq&=(qwq-1); 25 } 26 if (j+sum<=t){ 27 f[i|(1<<(k-1))][j+sum]+=f[i][j]; 28 if (f[i|(1<<(k-1))][j+sum]>=qaq) 29 f[i|(1<<(k-1))][j+sum]%=qaq; 30 } 31 } 32 int ans=0; 33 for (int i=0;i<=t;i++) 34 ans=(ans+f[(1<<n)-1][i])%qaq; 35 printf("%d\n",ans); 36 return 0; 37 }
神奇的代码
注意运算优先级,注意运算优先级,注意运算优先级!!!
时间: 2024-10-13 00:17:54