题解 抑郁刀法

传送门

来不及写这题了,先记个思路
好现在我补上了

首先对于那个20pts的状压:
同样很难想到,先预处理出染成同一种颜色合法的集合,然后令 \(dp[s][i]\) 为集合 \(s\) 染成 \(i\) 种颜色的方案数
转移考虑对于一个 \(s\),枚举其补集的子集 \(s_2\),那转移就是

\[dp[s|s2][i+1] += dp[s][i] \]

这部分是后面正解的重要基础

仔细观察这个图,发现度数为1的点貌似完全没用:它们一定会且仅会给答案乘上一个 \(k-1\)
所以这些点可以直接删掉
然后试着删度数为2的点
若一个度数为2的点 \(i\) 连接了 \(a\)\(b\),我们考虑这个 \(i\) 的贡献
删掉这个点若 \(a,b\) 同色,则 \(i\)\(k-1\) 情况与 \(a,b\) 异色
若异色,则 \(i\)\(k-2\) 情况与 \(a,b\) 异色
\(f(a, b), g(a, b)\) 分别表示它们同色时答案乘上的系数,异色时答案乘上的系数,那有

\[f=f_1f_2+(k-1)g_1g_2 \]

\[g=(k-2)g_1g_2 + f_1g_2 + f_2g_1 \]

在删掉度数为 \(2\) 的点时更新新边对应的 \(f(a, b)\) 即可

  • 当出现 \(n\) 个点,\(m\) 条边,且有形如 \(m \leqslant n+5\) 的限制时,这实际上是在说度数 \(\geqslant 3\) 的点很少
    首先考虑所有度数为1和2的点的贡献能不能直接算或设法合并掉,也即能不能把这张图删得仅剩度数 \(\geqslant 3\) 的点
    然后可以算出剩余点数的上界,如果 \(n\)\(m\) 相差不大,剩的点数也会很少,也许可以状压
    但不同的题如何合并掉度数为1和2的点应该就没什么规律了。也许对每条边记录一个或几个数组记录不同情况下这样一条边对答案的贡献系数?

具体地,用unordered_map存边,队列删点,动态分配边的编号
度数为1的点和自环可以直接累乘到答案上
重边可以直接合并
若合并一条边后某个点的度数由不合法变为合法,则将它入队
每次从队列中取出一个点删掉,所以复杂度是 \(O(n)\)
最后 \(O(nm3^{rest)}\) 状压,\(rest \leqslant 10\)

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#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, k;
const ll mod=1e9+7;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}

namespace force{
	ll ans;
	int col[N];
	pair<int, int> e[N];
	bool check() {
		for (reg i=1; i<=m; ++i)
			if (col[e[i].fir]==col[e[i].sec])
				return 0;
		return 1;
	}
	void dfs(int u) {
		if (u>n) {
			if (check()) ++ans;
			return ;
		}
		for (reg i=1; i<=k; ++i) {
			col[u]=i;
			dfs(u+1);
		}
	}
	void solve() {
		for (reg i=1; i<=m; ++i) e[i]=make(read(), read());
		dfs(1);
		printf("%lld\n", ans%mod);
		exit(0);
	}
}

namespace task1{
	int e[15][15], siz[15];
	ll fac[N], inv[N], dp[1<<10][15], ans;
	bool able[1<<10];
	inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
	void solve() {
		fac[0]=fac[1]=1; inv[0]=inv[1]=1;
		for (int i=2; i<=k; ++i) fac[i]=fac[i-1]*i%mod;
		for (int i=2; i<=k; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (int i=2; i<=k; ++i) inv[i]=inv[i-1]*inv[i]%mod;
		for (int i=1,u,v; i<=m; ++i) {
			u=read()-1; v=read()-1;
			e[u][++siz[u]]=v;
			e[v][++siz[v]]=u;
		}
		int lim=1<<n;
		for (reg s=1; s<lim; ++s) {
			for (reg i=0; i<n; ++i) if (s&(1<<i))
				for (reg j=1; j<=siz[i]; ++j) if (s&(1<<e[i][j])) goto jump;
			able[s]=1;
			jump: ;
		}
		for (reg s=1; s<lim; ++s) {
			if (able[s]) dp[s][1]=1;
			for (reg i=1; i<=n; ++i) if (dp[s][i]) {
				for (reg s2=(~s)&(lim-1),s0=(~s)&(lim-1); s2; s2=s0&(s2-1)) if (able[s2]) 
					md(dp[s|s2][i+1], dp[s][i]);
			}
		}
		for (int i=1; i<=n; ++i) md(ans, dp[lim-1][i]*C(k, i)%mod);
		printf("%lld\n", ans);
		exit(0);
	}
}

namespace task{
	int siz[15];
	int sta[N<<2], top, cnt[N], live[N], rk[N];
	ll fac[N], inv[N], dp[1<<11][15], ans, extra=1, base[1<<11];
	bool del[N], vis[N];
	queue<int> q;
	unordered_map<int, int> head[N];
	struct edge{mutable ll f, g; inline void build(ll a, ll b) {f=a; g=b;}}e[N<<2];
	struct grp{int to; ll f, g; inline void build(int v, ll f_, ll g_) {to=v; f=f_; g=g_;}}mp[15][15];
	inline bool operator < (grp a, grp b) {return a.to<b.to;}
	inline ll C(int n, int k) {return fac[n]*inv[k]%mod*inv[n-k]%mod;}
	void solve() {
		for (int i=1; i<=m*2+50; ++i) sta[++top]=i;
		fac[0]=fac[1]=1; inv[0]=inv[1]=1;
		for (int i=2; i<=k; ++i) fac[i]=fac[i-1]*i%mod;
		for (int i=2; i<=k; ++i) inv[i]=(mod-mod/i)*inv[mod%i]%mod;
		for (int i=2; i<=k; ++i) inv[i]=inv[i-1]*inv[i]%mod;
		for (int i=1,u,v; i<=m; ++i) {
			u=read(); v=read();
			++cnt[u]; ++cnt[v];
			e[sta[top]].build(0, 1);
			head[u][v]=sta[top--];
			//cout<<"uv: "<<u<<' '<<v<<endl;
			e[sta[top]].build(0, 1);
			head[v][u]=sta[top--];
		}
		pair<int, int> to[3]; int top2;
		for (int i=1; i<=n; ++i) q.push(i), vis[i]=1;
		int u;
		while (q.size()) {
			u=q.front(); q.pop();
			vis[u]=0;
			if (cnt[u]>=3 || cnt[u]==0 || del[u]) continue;
			//cout<<"u: "<<u<<endl;
			if (cnt[u]==1) {
				for (auto it:head[u]) {
					extra=extra*(1ll*(k-1)*e[it.sec].g%mod+e[it.sec].f)%mod;
					if (--cnt[it.fir]==0) del[it.fir]=1;
					else if (cnt[it.fir]<=2) q.push(it.fir);
					head[it.fir].erase(u);
					//cout<<"erase: "<<u<<endl;
				}
				--cnt[u]; del[u]=1;
			}
			else if (cnt[u]==2) {
				top2=0;
				for (auto it:head[u])
					to[++top2]=it; //, cout<<"it: "<<it.fir<<' '<<it.sec<<endl;
				int x=to[1].fir, y=to[2].fir;
				//cout<<"xy: "<<x<<' '<<y<<endl;
				ll f1=e[to[1].sec].f, g1=e[to[1].sec].g;
				ll f2=e[to[2].sec].f, g2=e[to[2].sec].g;
				ll f=(f1*f2%mod + 1ll*(k-1)*g1%mod*g2%mod)%mod;
				ll g=(1ll*(k-2)*g1%mod*g2%mod + f1*g2%mod + f2*g1%mod)%mod;
				head[to[1].fir].erase(u); head[to[2].fir].erase(u);
				//cout<<"erase: "<<u<<endl;
				sta[++top]=to[1].sec; sta[++top]=to[2].sec;
				sta[++top]=head[u][to[1].fir]; sta[++top]=head[u][to[2].fir];
				if (head[x].find(y)!=head[x].end()) {
					if (head[x].size()==1 || head[y].size()==1) {
						int t=head[x][y];
						f1=f, g1=g;
						f2=e[t].f, g2=e[t].g;
						f=(f1*f2%mod + 1ll*(k-1)*g1%mod*g2%mod)%mod;
						g=(1ll*(k-2)*g1%mod*g2%mod + f1*g2%mod + f2*g1%mod)%mod;
						sta[++top]=head[x][y]; sta[++top]=head[y][x];
						//cout<<"erase: "<<x<<' '<<y<<endl;
						head[x].erase(y); head[y].erase(x);
						extra=extra*f%mod;
						if ((cnt[x]-=2)==0) del[x]=1;
						if ((cnt[y]-=2)==0) del[y]=1;
						if (!vis[x]) q.push(x);
						if (!vis[y]) q.push(y);
					}
					else {
						int t=head[x][y];
						e[t].f=e[t].f*f%mod; e[t].g=e[t].g*g%mod;
						t=head[y][x];
						e[t].f=e[t].f*f%mod; e[t].g=e[t].g*g%mod;
						if (--cnt[x]==0) del[x]=1;
						if (--cnt[y]==0) del[y]=1;
						if (!vis[x]) q.push(x);
						if (!vis[y]) q.push(y);
					}
				}
				else {
					e[sta[top]].build(f, g);
					head[x][y]=sta[top--];
					e[sta[top]].build(f, g);
					head[y][x]=sta[top--];
				}
				del[u]=1;
			}
		}
		//cout<<"del: "; for (int i=1; i<=n; ++i) cout<<del[i]<<' '; cout<<endl;
		//cout<<"extra: "<<extra<<endl;
		top2=0;
		for (int i=1; i<=n; ++i) if (!del[i]) {
			//cout<<"live: "<<i<<endl;
			rk[i]=top2;
			live[top2++]=i;
		}
		//cout<<"top2: "<<top2<<endl;
		if (!top2) {printf("%lld\n", extra*k%mod); exit(0);}
		for (int i=0; i<top2; ++i) {
			for (auto it:head[live[i]])
				mp[i][++siz[i]].build(rk[it.fir], e[it.sec].f, e[it.sec].g);
			//sort(mp[i]+1, mp[i]+siz[i]+1);
		}
		//cout<<"---mp---"<<endl;
		//for (int i=0; i<top2; ++i) {
		//	for (int j=1; j<=siz[i]; ++j) cout<<mp[i][j].to<<' '; cout<<endl;
		//}
		int lim=1<<top2;
		for (reg s=1; s<lim; ++s) {
			base[s]=1ll;
			for (reg i=0; i<top2; ++i) if (s&(1<<i))
				for (reg j=1; j<=siz[i]; ++j) if (s&(1<<mp[i][j].to) && mp[i][j].to>i)
					base[s]=base[s]*mp[i][j].f%mod;
			//cout<<"base: "<<bitset<5>(s)<<' '<<base[s]<<endl;
		}
		for (reg s=1; s<lim; ++s) {
			dp[s][1]=base[s];
			for (reg i=1; i<=top2; ++i) if (dp[s][i]) {
				for (reg s2=(~s)&(lim-1),s0=(~s)&(lim-1); s2; s2=s0&(s2-1)) if (base[s2]) {
					ll tem=dp[s][i]*base[s2]%mod;
					for (reg j=0; j<top2; ++j) if (s&(1<<j))
						for (reg k=1; k<=siz[j]; ++k) if (s2&(1<<mp[j][k].to))
							tem=tem*mp[j][k].g%mod;
					md(dp[s|s2][i+1], tem);
				}
			}
		}
		for (int i=1; i<=top2; ++i) md(ans, dp[lim-1][i]*C(k, i)%mod);
		printf("%lld\n", ans*extra%mod);
		exit(0);
	}
}

signed main()
{
	freopen("knife.in", "r", stdin);
	freopen("knife.out", "w", stdout);
	
	n=read(); m=read(); k=read();
	task::solve();
	
	return 0;
}
posted @ 2021-09-13 06:33  Administrator-09  阅读(10)  评论(0编辑  收藏  举报