题解 小L的有向图

传送门

挺思路的题,正解比暴力好写

对于 \(n\leqslant 9\):全排列枚举拓扑序,考虑排在前面的点向排在后面的点连出的边的状态可以任意,于是可以统计
然后正解:状压枚举拓扑序,考虑向一个已经确定的点集中加入一个点,则这个点向已经确定的点集中连出的边都一定不能选
而其它的可以任意,于是可以统计出加入一个点后的方案数,于是得解

  • 拓扑序之类的东西是可以用转移顺序表示出来的(废话),可以借此完成一些对拓扑序有要求的DP
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define fir first
#define sec second
#define make make_pair
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
int cnt[N], head[N], size;
const ll mod=998244353;
struct edge{int to, next; bool vis;}e[N<<1];
inline void add(int s, int t) {e[++size].to=t; e[size].next=head[s]; head[s]=size;}
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}

namespace force{
	int tot;
	ll ans;
	bool vis[N];
	void dfs(int u) {
		if (u>n) {++ans; return ;}
		for (int i=1; i<=n; ++i) if (!vis[i] && !cnt[i]) {
			vis[i]=1;
			for (int j=head[i],v; ~j; j=e[j].next) if (!e[j].vis) --cnt[e[j].to];
			dfs(u+1);
			for (int j=head[i],v; ~j; j=e[j].next) if (!e[j].vis) ++cnt[e[j].to];
			vis[i]=0;
		}
	}
	void solve() {
		memset(head, -1, sizeof(head));
		for (int i=1,x,y; i<=m; ++i) {
			x=read(); y=read();
			add(x, y);
			++cnt[y];
		}
		int lim=1<<m;
		for (int s=0; s<lim; ++s) {
			// cout<<"s: "<<bitset<4>(s)<<endl;
			for (int i=0; i<m; ++i) if (s&(1<<i)) e[i+1].vis=1, --cnt[e[i+1].to];
			dfs(1);
			for (int i=0; i<m; ++i) if (s&(1<<i)) e[i+1].vis=0, ++cnt[e[i+1].to];
			// cout<<"ans: "<<ans<<endl;
		}
		printf("%lld\n", ans%mod);
		exit(0);
	}
}

namespace task{
	ll dp[1<<22];
	int cnt[30], out[N], siz[1<<22];
	void solve() {
		for (int i=1,x,y; i<=m; ++i) {
			x=read()-1; y=read()-1;
			++cnt[x]; out[x]|=1<<y;
		}
		int lim=1<<n;
		for (int s=1,s2=1; s<lim; ++s,s2=s) do {++siz[s]; s2&=s2-1;} while (s2);
		dp[0]=1;
		for (int s=0; s<lim; ++s) {
			for (int i=0; i<n; ++i) if (!(s&(1<<i))) {
				dp[s|(1<<i)]=(dp[s|(1<<i)]+dp[s]*(1ll<<siz[out[i]&(~s)]))%mod;
			}
		}
		printf("%lld\n", dp[lim-1]);
		exit(0);
	}
}

signed main()
{
	freopen("topology.in", "r", stdin);
	freopen("topology.out", "w", stdout);

	n=read(); m=read();
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2021-10-10 15:34  Administrator-09  阅读(7)  评论(0编辑  收藏  举报