题解 图

传送门

这个特殊性质指向性极强
可以考虑大力最小表示法记录后 \(12\) 个点的联通情况
这样看起来是不太能过的,那么怎么优化呢?
请读者认真思考后再看下面的解决方法
我们可以通过爆搜状态数实际上界的方法从本质上优化复杂度!爆搜可知有效状态数只有 \(1e5\) 个,于是可以 AC 了。
挺好。

然后正解比较神仙,没见过应该不容易想出来
考虑这个对 \(2\) 取模
我们想保留一些点,使得这些点形成恰好一个连通块,求方案数 \(\bmod 2\)
那么令一种方案的权值为 \(2^{连通块数-1}\) 即可
发现这个 -1 不好处理,那么可以对 \(4\) 取模最后除以 2
那么这个带权方案数怎么求呢?发现就是对每个连通块黑白染色的方案数!
那么每个点只有不选或黑白染色三种情况,可以在 \(O(n^23^{12})\) 的复杂度内解决

  • 一些对 \(2\) 取模的题,也许可以考虑给每个方案赋 \(2^{\cdots}\) 的权,使得只有合法方案的 \(\cdots\) 为 0
    那么什么和 2 的次幂有关呢?一个例子是黑白染色
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#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;
vector<int> to[N];
pair<int, int> e[N];
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a,b>>=1) if (b&1) ans=ans*a; return ans;}

namespace force{
	bool vis[N];
	int dsu[N], ans;
	inline int find(int p) {return dsu[p]==p?p:dsu[p]=find(dsu[p]);}
	void solve() {
		int lim=1<<n;
		for (int s=1; s<lim; ++s) {
			for (int i=1; i<=n; ++i) dsu[i]=i;
			for (int i=1; i<=n; ++i)
				if (s&(1<<(i-1))) vis[i]=1;
				else vis[i]=0;
			for (int i=1; i<=m; ++i) if (vis[e[i].fir]&&vis[e[i].sec]) dsu[find(e[i].fir)]=find(e[i].sec);
			int rot=-1;
			for (int i=1; i<=n; ++i) if (vis[i]) {
				if (rot==-1) rot=find(i);
				else if (rot!=find(i)) goto jump;
			}
			++ans;
			jump: ;
		}
		cout<<(ans&1)<<endl;
	}
}

namespace task{
	int f[52][531445], sta[15], tem[15], id[55], l, r, ans;
	inline void add(int& a, int b) {a=(a+b)%4;}
	void decode(int s) {for (int i=1; i<=12; ++i) sta[i]=s%3, s/=3;}
	int encode() {int ans=0; for (int i=12; i; --i) ans=ans*3+tem[i]; return ans;}
	bool inrange(int i) {return l<=i&&i<=r;}
	void solve() {
		int lim=qpow(3, 12);
		l=1, r=12;
		for (int i=1; i<=12; ++i) id[i]=12-i+1;
		for (int s=0; s<lim; ++s) {
			decode(s);
			for (int i=1,a,b; i<=m; ++i) if (inrange(a=e[i].fir)&&inrange(b=e[i].sec)&&(sta[id[a]]&&sta[id[b]])&&sta[id[a]]!=sta[id[b]]) goto jump;
			f[13][s]=1;
			jump: ;
		}
		for (int i=13; i<=n; ++i) {
			l=i-12, r=i-1;
			for (int j=1; j<=12; ++j) id[i-j]=j;
			for (int s=0; s<lim; ++s) if (f[i][s]) {
				decode(s);
				// cout<<"tr: "<<i<<' '; for (int j=2; j; --j) cout<<sta[j]; cout<<' '<<f[i][s]<<endl;
				for (int j=1; j<12; ++j) tem[j+1]=sta[j];
				// color to 1
				for (auto it:to[i]) if (inrange(it)&&sta[id[it]]&&sta[id[it]]!=1) goto jump1;
				tem[1]=1; add(f[i+1][encode()], f[i][s]); /* cout<<"to 1: "; for (int j=2; j; --j) cout<<tem[j]; cout<<' '<<endl; */ jump1:
				// color to 2
				for (auto it:to[i]) if (inrange(it)&&sta[id[it]]&&sta[id[it]]!=2) goto jump2;
				tem[1]=2; add(f[i+1][encode()], f[i][s]); /* cout<<"to 2: "; for (int j=2; j; --j) cout<<tem[j]; cout<<' '<<endl; */ jump2:
				// not choose
				tem[1]=0; add(f[i+1][encode()], f[i][s]); /* cout<<"to 0: "; for (int j=2; j; --j) cout<<tem[j]; cout<<' '<<endl; */
			}
		}
		for (int s=0; s<lim; ++s) add(ans, f[n+1][s]);
		cout<<(ans-1)/2<<endl;
	}
}

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

	n=read(); m=read();
	for (int i=1,u,v; i<=m; ++i) {
		e[i].fir=u=read(), e[i].sec=v=read();
		to[u].pb(v); to[v].pb(u);
	}
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2022-04-13 18:55  Administrator-09  阅读(3)  评论(0编辑  收藏  举报