ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-04-17 12:57:25  阅读:308  来源: 互联网

标签:tarjan 求割点 int cnt dfn low 反色 bz fo


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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有