其他分享
首页 > 其他分享> > 「HAOI2018」反色游戏(找性质+tarjan求割点)

「HAOI2018」反色游戏(找性质+tarjan求割点)

作者:互联网

https://loj.ac/problem/2524

类似于NOI ONline T1,
对于一棵树,不难发现,当黑点个数为奇数时,一定无解,为偶数时,一定可以调整出唯一一组解。

如果额外加一些非树边,那么不管非树边怎么选,树边都有办法调整,所以方案数是\(2^{非树边数}\)

考虑删掉一个点时,也就是看删掉这个点之后新形成的联通块有多少个,有没有联通块有奇数个黑点。

如果知道tarjan求割点的方法,不难得到以下解法:

对于一个联通块,随便选一个点作为根开始tarjan。

对于根,如果删掉它,它的dfs树上的每个子树会形成一个联通块。

对于dfs树上非根的点\(x\),如果删掉它,那些\(low[y]\ge dfn[x]\)的子节点\(y\)的子树会形成一个联通块,然后剩下的所有点会形成一个联通块。

这样我们只要tarjan一遍,顺便维护一些信息(子树黑点个数,会独立的子树的信息),就可以得到答案了。

Code:

#include<bits/stdc++.h>
#define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
#define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
#define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
#define ll long long
#define pp printf
#define hh pp("\n")
using namespace std;

const int mo = 1e9 + 7;

ll ksm(ll x, ll y) {
	ll s = 1;
	for(; y; y /= 2, x = x * x % mo)
		if(y & 1) s = s * x % mo;
	return s;
}

const int N = 2e5 + 5;

int T;

int n, m, x, y, r[N];
int fi[N], to[N * 2], nt[N * 2], tot = 1;
char str[N]; int a[N];

void link(int x, int y) {
	nt[++ tot] = fi[x], to[tot] = y, fi[x] = tot;
}

int low[N], dfn[N], dfn0;

void cl() {
	fo(i, 1, n) r[i] = fi[i] = low[i] = dfn[i] = 0;
	tot = 1;
}

int s[N], cnt[N][2], bz[N], G;

void dg(int x, int la) {
	bz[x] = G;
	low[x] = dfn[x] = ++ dfn0;
	s[x] = a[x];
	cnt[x][0] = cnt[x][1] = 0;
	for(int i = fi[x]; i; i = nt[i]) if(i != la) {
		int y = to[i];
		if(!dfn[y]) {
			dg(y, i ^ 1);
			s[x] ^= s[y];
			low[x] = min(low[x], low[y]);
			if(low[y] >= dfn[x]) {
				cnt[x][s[y]] ++;
			}
		} else low[x] = min(low[x], dfn[y]);
	}
}

int ltk;

void work() {
	scanf("%d %d", &n, &m);
	fo(i, 1, m) {
		scanf("%d %d", &x, &y);
		link(x, y); link(y, x);
		r[x] ++, r[y] ++;
	}
	scanf("%s", str + 1);
	fo(i, 1, n) a[i] = str[i] - '0';
	ltk = 0;
	fo(i, 1, n) if(!dfn[i]) {
		ltk ++;
		G = i, dg(i, 0);
	}
}

int main() {
	scanf("%d", &T);
	fo(ii, 1, T) {
		cl();
		work();
		int c1 = 0;
		fo(i, 1, n) if(bz[i] == i)
			c1 += (s[i] == 1);
		int c = m - n + ltk;
		pp("%lld ", c1 > 0 ? 0 : ksm(2, c));
		fo(i, 1, n) {
			if(!r[i]) {
				pp("%lld ", c1 > 0 ? 0 : ksm(2, c));
			} else {
				if(!cnt[i][1] && (c1 - s[bz[i]]) == 0 && (s[bz[i]] ^ a[i]) == 0) {
					pp("%lld ", ksm(2, c - r[i] + 1 + cnt[i][0] - (bz[i] == i)));
				} else pp("0 ");
			}
		}
		hh;
	}
}

标签:tarjan,求割点,int,cnt,dfn,low,反色,bz,fo
来源: https://www.cnblogs.com/coldchair/p/12719130.html