题解 Lighthouse(留坑)

传送门

板子题,但是不会……容斥该补了

\(m \leqslant 20\),可以状压枚举子集
可以依据集合大小容斥
有环且大小不为 \(n\) 的集合不合法?

柿子暂时不会推,溜了溜了

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 10000010
#define ll long long
#define reg register int
#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;
const ll mod=1e9+7;

namespace force{
	ll inv[20];
	ll rec[18][1<<18];
	bool mp[18][18];
	ll dfs(int u, int vis) {
		if (vis==(1<<n)-1) {return mp[0][u];}
		if (~rec[u][vis]) return rec[u][vis];
		ll ans=0;
		for (reg i=0; i<n; ++i) 
			if (mp[u][i] && !(vis&(1<<i))) ans+=dfs(i, vis|(1<<i));
		ans%=mod;
		rec[u][vis]=ans;
		return ans;
	}
	void solve() {
		memset(rec, -1, sizeof(rec));
		inv[0]=inv[1]=1;
		for (reg i=2; i<=n; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (reg i=0; i<n; ++i)
			for (reg j=0; j<n; ++j)
				if (i!=j) mp[i][j]=1;
		for (reg i=1,u,v; i<=m; ++i) {
			u=read()-1; v=read()-1;
			mp[u][v]=mp[v][u]=0;
		}
		printf("%lld\n", (dfs(0, 1)%mod*inv[2])%mod);
		exit(0);
	}
}

namespace task{
	pair<int, int> e[N];
	int cnt[N], sta[N], stop, fa[N];
	ll fac[N], ans, inv[N];
	bool vis[N];
	inline int find(int p) {return fa[p]==p?p:fa[p]=find(fa[p]);}
	void solve() {
		fac[0]=fac[1]=1; inv[0]=inv[1]=1; inv[2]=(mod-mod/2)*inv[mod%2]%mod;
		for (reg i=1; i<=n; ++i) fac[i]=fac[i-1]*i%mod;
		for (reg i=1; i<=n; ++i) fa[i]=i;
		for (reg i=1; i<=m; ++i) e[i]=make(read()-1, read()-1);
		for (reg s=0,lim=1<<m,k,cnt2,s2; s<lim; ++s) {
			//cout<<"for: "<<s<<endl;
			bool flag=0; stop=0;
			//memset(cnt, 0, sizeof(int)*(n+5));
			//memset(vis, 0, sizeof(bool)*(n+5));
			//for (reg i=1; i<=n; ++i) fa[i]=i;
			while (stop) cnt[sta[stop--]]=0;
			for (reg i=0; i<m; ++i) if (s&(1<<i)) {
				sta[++stop]=e[i+1].fir;
				sta[++stop]=e[i+1].sec;
				if (++cnt[e[i+1].fir]>2) goto jump;
				if (++cnt[e[i+1].sec]>2) goto jump;
			}
			//cout<<"s: "<<s<<endl;
			while (stop) cnt[sta[stop--]]=0;
			for (reg i=0,f1,f2; i<m; ++i) if (s&(1<<i)) {
				sta[++stop]=e[i+1].fir;
				sta[++stop]=e[i+1].sec;
				f1=find(e[i+1].fir), f2=find(e[i+1].sec);
				if (f1==f2) flag=1;
				fa[f1]=f2;
			}
			k=0;
			for (reg i=0,t; i<m; ++i) if (s&(1<<i)) {
				if (!vis[t=find(e[i+1].fir)]) vis[t]=1, ++k;
				if (!vis[t=find(e[i+1].sec)]) vis[t]=1, ++k;
			}
			cnt2=0; s2=s;
			if (s) do {s2&=s2-1; ++cnt2;} while (s2);
			if (flag) {
				if (k!=1 || cnt2!=n) goto jump;
				//cout<<"k: "<<bitset<20>(s)<<endl;
				for (reg i=0; i<m; ++i) if (s&(1<<i)) ++cnt[e[i+1].fir], ++cnt[e[i+1].sec];
				for (reg i=0; i<m; ++i) if (s&(1<<i) && (cnt[e[i+1].fir]!=2||cnt[e[i+1].sec]!=2)) goto jump;
				//cout<<"live: "<<bitset<20>(s)<<endl;
			}
			ans=(ans+(cnt2&1?-1ll:1ll)*(1ll<<k)%mod*fac[max(n-cnt2-1, 0)]%mod)%mod;
			jump: ;
			while (stop) cnt[sta[stop]]=0, fa[sta[stop]]=sta[stop], vis[sta[stop--]]=0;
		}
		//cout<<"ans: "<<ans<<endl;
		printf("%lld\n", (ans*inv[2]%mod+mod)%mod);
		exit(0);
	}
}

signed main()
{
	n=read(); m=read();
	task::solve();
	
	return 0;
}
posted @ 2021-08-27 21:39  Administrator-09  阅读(21)  评论(0编辑  收藏  举报