一.闲话
今天这题着实有点难想啊。。。(也许我太菜了?qwq)
字数警告
二.题解
首先,我们先考虑答案为0的情况——存在一个所以边的边权都确定的环,其中的边权异或和为1
考虑到做这个,边权未定的边并无任何影响,所以,我们先把这些边放到一边,先把边权确定的边全部连上,然后就开始做这道题了。
首先,我们不可能找每个环,并计算他们各自的异或和,这样很明显会T(举个栗子,我们构造一个完全图,就一共有(个环))[任意两个以上的点就会组成一个环]
稍微算算就知道这是不能枚举出来的,那么我们该怎么办呢?
我也不知道呀qwq,然后参考了网上这篇题解的方法,我们把边权转换为点权判断。这篇题解已经说的很清楚了,在这里我再解释下。
我们随意设置点权,使得任意一条边的两个端点的点权的异或值等于这条边的边权
这么设置有什么好处?
我们知道若一条边的点权等于了两端点的异或值,那么,就有,一个环:
{}
边权异或和=
V表示的是我们设置的点权
由于异或满足结合律,所以上面的答案值就等于0,正好就是我们所需要的
也就是说,只要存在一组点权,使得一条边的两端点的点权异或值等于这条边的边权,那么,我们点形成的环的异或和就一定为0
那么,是否存在一个环的边权异或和为0却不能满足上面的点权设置的方法?
答案是不存在的,我们反证一下就可以证明了:
假设存在,那么如果我们让矛盾的边的一个端点x同时具有两个点权来抹除矛盾。
那么,边权和==1
不满足假设。
那么又有问题来了,如果有两条矛盾的边,那么边权和不就为0了么?
如果边的结构是一颗树的话,那么是一定合法的,因为每个点的点权都可以通过它的父亲唯一确定,而一定不会出现矛盾。
而你画一个环,里面有n条边(看题面定义),那么,如果有矛盾出现的话,一定是出在最后一条边,因为之前的n-1条边构成了一个树结构,不可能出现矛盾,所以最多只有一条边出现矛盾,证毕。
如果没有环呢?那么,就说明,当前的结构就是一颗树,所以,按照上面的说的,就一定不会出现矛盾,反过来说就是,如果出现矛盾了,那么一定是至少有一个环的边权异或和为1
所以,我们只需要通过设置点权就可以解决了,那么我们怎么设置呢?
对于第一个点,我们应该设置0还是1?
答案是随便,为什么?如果存在一种设置点权的方案的话,我们如果将所有点取反,那么,我们会发现,方案依旧合法,原因是,一条边由两个点权异或而来,两个点都取反一次,就相当于边权取反两次,跟原来的边权一样,所以,如果第一个点为0都不能设置点权,那么,第一个点为1也不能够设置
那么,我们再来说说怎么设置点权的问题。
一开始,我们把所有边权确定的边进行了连接,那么,很明显,如果两个点之间有边,若其中一个点的边权确定了,那么,由于边权也确定了,另一个点权也就确定了,所以,我们先直接另第一个点为0/1,之后再把每个点的点权算出来,中间看看有没有矛盾的地方就行了。
由于,我们连的边,不一定会使得所有点在同一个连通块,所以,我们要对每个连通块进行独立的判断。
然后,我们考虑如何计算答案。
我们现在只需要考虑边权不确定(w==-1)的边
我们分为两个情况:
1.该边两个端点在同一个连通块中
这个时候,由于我们在一个连通块中添加了一条边,所以,现在必定有一个环包含这条边,又由于这条边的两个端点的异或和也已经固定了,所以该边边权只有一种取法。
2.该边两个端点不在同一个连通块中
这个时候,这条边起的作用仅仅是连通了两个连通块,不会构成环,所以,该边取0/1都行,答案*2,并把两个连通块合并成一个联通块(因为这条边连通了两连通块,并查集实现)
那么,我们又如何保证如果后面添加一条边后,使得现在的这条边包含在了一个环中的情况合法?如果是这样的话,那么这条边的权值不就确定了么?那么我们难道要对这个情况答案/2吗?
答案是否定的,还记得我们之前说的吗,如果我们把一个连通块里面的点的点权全部取反,那么还是满足条件的,也就是说,如果我们把这条边连接的两个联通块中的一个联通块的点权全部取反,另一个不变,那么依旧满足条件。
所以,如果之后添加边使得我们现在的边处于一个环中并且这条边的边权不等于两端点的点权异或值的话,那么我们把其中一个连通块的所有点权取反后,就可以满足条件了。
这样,我们就把答案算出来了~
(一开始我傻乎乎的想搞无向图缩环,还推了公式qwq,结果后面发现,还要判断小环是否成立qwq)
代码:
#include<bits/stdc++.h> using namespace std; const int N=1e6+1,mod=998244353; struct node{ int v,w,nex; }t[N<<1]; int U[N],V[N],W[N]; int las[N],len,tim; int vis[N];bool val[N]; inline void add(int u,int v,int w){ t[++len]=(node){v,w,las[u]},las[u]=len; } inline void dfs(int x){ vis[x]=tim; for(int i=las[x];i;i=t[i].nex){ int v=t[i].v,w=t[i].w; if(vis[v]&&val[v]^val[x]!=w){//矛盾输出0即可 puts("0"); exit(0);//强制结束程序 } if(vis[v]){ continue; } val[v]=val[x]^w; dfs(v); } return; } inline int find(int x){ return vis[x]==x?x:vis[x]=find(vis[x]); } inline void merge(int x,int y){ vis[find(x)]=find(y); } int main(){ //考虑无向图缩环 //对于环里面的每条边,只要1的个数为偶数即可 //1.当1的决定数为奇数,还剩x条边 //ansi=C(x,1)+C(x,3)+C(x,5)+... //2.当1的决定数为偶数,还剩x条边 //ansi=C(x,0)+C(x,2)+C(x,4)+... //若x为奇数,则,答案是2^(x-1) //若x为偶数,推导=>多出来的那个是0,ans=2^(x-2),多出来的那个是1,ans=2^(x-2) //故,答案皆为2^(x-1) //若x=0直接判断即可~ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;++i){ int u,v,w; scanf("%d%d%d",&u,&v,&w); if(w!=-1){ add(u,v,w),add(v,u,w); } U[i]=u,V[i]=v,W[i]=w; } for(int i=1;i<=n;++i){//check if(!vis[i]){ tim=i; dfs(i); } } //如果在同一个联通块,则填的边就已经被确定了。。。(你都连通了,当然边权也就确定了) //所以只考虑不在同一个联通块的点 int ans=1; for(int i=1;i<=m;++i){ if(vis[find(U[i])]!=vis[find(V[i])]){ ans=(2LL*ans)%mod; merge(U[i],V[i]); } } printf("%d",ans); return 0; }