题目传送门

题目大意

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

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

有 \(t\) 组数据,\(t\le 5,n,m\le 10^5\)。

思路

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

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

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

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

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

  • 连通块个数

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

  • 判断合法性

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

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

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

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


用 \(\text{Tarjan}\) 实现,时间复杂度 \(\Theta(Tn)\)。

\(\texttt{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 ('\n'),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;
}

最新文章

  1. 流行的JavaScript库 ——jQuery
  2. [Java Web] 1、Web开发初识——一大堆历史和技术名词
  3. object-c学习笔记
  4. PAT-乙级-1047. 编程团体赛(20)
  5. Android简单计算器
  6. ios开发--网页中调用JS与JS注入
  7. Android 自定义View修炼-自定义View-带百分比进度的圆形进度条(采用自定义属性)
  8. [转] Android root 原理
  9. hdfs格式化hadoop namenode -format错误
  10. sessionStorage、localStorage 存储及如何存储数组与对象
  11. Windows AD域升级方
  12. Codeforces 626D Jerry&#39;s Protest(暴力枚举+概率)
  13. 命令行更新node和npm
  14. bzoj3718 树状数组
  15. C#设计模式(11)——装饰者模式
  16. win10 安装 oracle 11g
  17. EF 跨库查询
  18. 执行Import-SPWeb报错的解决办法
  19. BZOJ4951 Wf2017Money for Nothing(决策单调性)
  20. 选项卡栏控制器(UITabBarController)

热门文章

  1. 面试必问题:JS防抖与节流
  2. java的stream的使用
  3. vue 基础入门(一)
  4. VS在调试桌面程序时,cout到控制台方法
  5. Servlet 之文件下载
  6. fetch ios低版本兼容cannot clone a disturbed response
  7. Vue Abp vNext获取当前登录用户
  8. 利用nginx 来实现内网yum源(反向代理)
  9. k8s 部署elasticsearch报 max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
  10. 洛谷P1309——迷宫(傻瓜DFS)