「PKUWC2019」拓扑序计数(状压dp)

考场只打了 \(52\) 分暴力。。。\(ljc\) 跟我说了一下大致思路,我回去敲了敲。

\(f[i]\) 表示状态为 \(i\) 时的方案数。我们用二进制 \(0/1\) 表示不选/选点 \(i\)

我们设 \(j\in i\) 且拓扑序最小。

\[f[i]=\sum f[i\text{^}2^j]\times 2^{i\&w[j]} \]

为什么这个是对的呢?

因为 \(j\) 连出的那些没有连向状态 \(i\) 的边一定会被删去,然后那些连向边只用 \(2^i\) 搞一搞。时间复杂度 \(O(n2^n)\)

刚手玩了几组数据好像没错,先放上来好了。

\(Code\ Below:\)

#include <bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,m,lim,bin[30],w[30],f[1<<20],cnt[1<<20];

int main()
{
	scanf("%d%d",&n,&m);lim=(1<<n)-1;
	int x,y;
	for(int i=0;i<m;i++){
		scanf("%d%d",&x,&y);
		x--;y--;w[x]|=1<<y;
	}
	bin[0]=1;
	for(int i=1;i<=n;i++) bin[i]=bin[i-1]<<1;
	for(int i=1;i<=lim;i++) cnt[i]=cnt[i>>1]+(i&1);
	f[0]=1;
	for(int i=1;i<=lim;i++)
		for(int j=0;j<n;j++)
			if(i&bin[j]) f[i]=(f[i]+1ll*f[i^bin[j]]*bin[cnt[i&w[j]]]%mod)%mod;
	printf("%d\n",f[lim]);
	return 0;
}
posted @ 2019-01-21 19:34  Owen_codeisking  阅读(420)  评论(0编辑  收藏  举报