zoukankan      html  css  js  c++  java
  • 题解 [HAOI2018]反色游戏

    题目传送门

    题目大意

    给出一个 (n) 个点 (m) 条无向边的图,每个点都有一个 (in [0,1]) 的权值,每次可以选择一条边,然后将该边相连两点权值异或上 (1)。问有多少种选择方法使得每个点的权值都变为 (0)。(每条边只能选择一次)

    但是这个问题太简单了,所以你要求删掉每个点以及它连出的边之后的答案。

    (t) 组数据,(tle 5,n,mle 10^5)

    思路

    这道题不是很难写,但是很难想。

    我们先考虑一棵树的答案,你发现这样方案是唯一的,因为你从叶子节点开始考虑,如果当前点是黑的,那就选择父亲边,否则就不能选。然后你发现有解的充要条件就是黑点是偶数个。

    考虑一个 (n)(m) 边的连通图,你发现如果我们钦点出 (n-1) 条边作为树边,那么它们是惟一的。对于剩下的 (m-n+1) 条边,你可以任意选择要或不要(相当于改变两个点的初始颜色),然后改变那 (n-1) 条边使得合法即可。所以答案就是 (2^{m-n+1}) ,判断有解的方法同上。

    对于一个不连通图,你发现如果有解的话答案就是 (2^{m-n+t})。其中 (t) 是连通块个数。显然。

    考虑删掉一个点以及相连的边后的方案数,实际上就是如何判断合不合法以及删掉之后的连通块个数。

    • 连通块个数

    你发现这个东西只跟割边有关,设 ( ext{cnt}(i)) 表示点 (i)( ext{dfs}) 树上与儿子节点相连的割边个数,你发现答案连通块个数就增加了 ( ext{cnt}(i)) 个。(如果 (i= ext{root}) 的话 ( ext{cnt}(i)) 需要减 (1) 因为它连通块增加个数会少一个,因为它没有父亲)

    • 判断合法性

    首先需要判断除了该点所属外的连通块的合法性。

    然后判断分出来的子树合法性。

    然后判断剩下的一块合法性就好了。

    合法性判断同上文,看连通块里面 (1) 的个数就好了。


    ( ext{Tarjan}) 实现,时间复杂度 (Theta(Tn))

    ( exttt{Code})

    #include <bits/stdc++.h>
    using namespace std;
    
    #define Int register int
    #define mod 1000000007
    #define MAXN 100005
    
    template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
    template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
    template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
    
    vector <int> G[MAXN];
    int n,m,rt,ind,a[MAXN],f[MAXN],d[MAXN],bin[MAXN],dfn[MAXN],low[MAXN],deg[MAXN],cnt[MAXN],bel[MAXN],sum[MAXN];
    
    void Link (int x,int y){
    	deg[x] ++,deg[y] ++;
    	G[x].push_back (y),G[y].push_back (x);
    }
    
    void Tarjan (int u){
    	bel[u] = rt,dfn[u] = low[u] = ++ ind,sum[u] = a[u];
    	for (Int v : G[u]){
    		if (!dfn[v]){
    			Tarjan (v);
    			sum[u] += sum[v];
    			low[u] = min (low[u],low[v]);
    			if (low[v] >= dfn[u]) cnt[u] ++,f[u] += sum[v],d[u] |= (sum[v] & 1);//u->v是割边 
    		}
    		else low[u] = min (low[u],dfn[v]);
    	}
    	cnt[u] -= (u == rt);
    } 
    
    signed main(){
    	int t;read (t);
    	bin[0] = 1;for (Int i = 1;i <= MAXN - 5;++ i) bin[i] = 2 * bin[i - 1] % mod;
    	while (t --> 0){
    		read (n,m);int val = 0,tot = 0;
    		for (Int i = 1,u,v;i <= m;++ i) read (u,v),Link (u,v);
    		for (Int i = 1;i <= n;++ i) scanf ("%1d",&a[i]);
    		for (Int i = 1;i <= n;++ i) if (!dfn[i]) rt = i,Tarjan (i),tot ++,val += (sum[i] & 1);
    		write (val ? 0 : bin[m - n + tot]);
    		for (Int i = 1;i <= n;++ i){
    			putchar (' ');
    			if (d[i]) putchar ('0');
    			else if (val - (sum[bel[i]] & 1)) putchar ('0');
    			else if ((sum[bel[i]] - f[i] - a[i]) & 1) putchar ('0');
    			else write (bin[(m - deg[i]) - (n - 1) + tot + cnt[i]]);
    		} 
    		putchar ('
    '),ind = 0;
    		for (Int i = 1;i <= n;++ i) G[i].clear (),f[i] = d[i] = cnt[i] = deg[i] = dfn[i] = low[i] = 0;
    	}
    	return 0; 
    }
    
  • 相关阅读:
    php中的md5()的安全问题
    爆破之中文转换成字母脚本
    MYSQL数据库封装类
    SQL执行结果操作
    原生和jquery 的 ajax
    Ajax实例OR技术原理 转自 (http://blog.csdn.net/evankaka )
    JQuery常用事件
    分页原理
    JQuery中$.ajax()方法参数
    session和cookie的作用和原理
  • 原文地址:https://www.cnblogs.com/Dark-Romance/p/13679326.html
Copyright © 2011-2022 走看看