Jzoj5230 队伍统计

227 篇文章 3 订阅
153 篇文章 0 订阅

现在有n个人要排成一列,编号为1->n 。但由于一些不明原因的关系,人与人之间可能存在一些矛盾关系,具体有m条矛盾关系(u,v),表示编号为u的人想要排在编号为v的人前面。要使得队伍和谐,最多不能违背k条矛盾关系(即不能有超过k条矛盾关系(u,v),满足最后v排在了u前面)。问有多少合法的排列。答案对10^9+7取模。

对应100%的数据,n,k<=20,m<=n*(n-1),保证矛盾关系不重复。

发现数据很小,可以用状压dp

我们设f[S][i]表示已经有i对矛盾关系,已经被选的集合为S的方案数

那么显然转移可以用bitset预处理一下

让后就可以卡过去

#include<stdio.h>
#include<time.h>
#include<bitset>
#define M 1000000007
using namespace std;
int f[1<<20][21],ans=0;
int n,m,k;
bitset<21> s[21],p;
inline void add(int& x,int y){ x=(x+y)%M; }
int main(){
	freopen("count.in","r",stdin);
	freopen("count.out","w",stdout);
	scanf("%d%d%d",&n,&m,&k);
	for(int x,y,i=0;i<m;++i){
		scanf("%d%d",&x,&y);
		s[--x][--y]=1;
	}
	f[0][0]=1;
	for(int S=0;S<(1<<n);++S){
		for(int i=0;i<=k;++i)
		if(f[S][i]) 
			for(int j=0;j<n;++j)
				if(~S&(1<<j)){
					p=S;
					int t=(s[j]&p).count();
					if(i+t<=k) add(f[S|(1<<j)][i+t],f[S][i]);
				}
	}
	for(int i=0;i<=k;++i) add(ans,f[(1<<n)-1][i]);
	printf("%d\n",ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值