首先转化一波:
对于1操作,我们求原图的生成树,然后对于每条非树边记下它和树边形成的环,这样的操作共有 $m-n+1$ 个;
对于2操作,我们考虑对于 $n$ 个结点,把和这个结点相连的边状态反转。
不难发现任何操作序列都可以拆成这 $m+1$ 个操作,且显然每个操作最多进行一次,所以只用考虑这 $m+1$ 个操作就可以了。
直接跑异或方程组消元就可以做到 $O(\frac{Tm^3}{w})$。
怎么优化呢?手玩一下可以发现:如果和每个点相连的堵塞边条数都是偶数,那么一定有解(证明可以看官方题解反正我也不会证)
而1操作是不会改变这个值的奇偶性的,因此只用考虑2操作,复杂度下降为 $O(\frac{Tn^3}{w})$。
一个简单的 trick:跑高斯消元的时候把所有全零式子扔到最后,那么判有解/无解只要检查最后的式子即可。
#include#include #include #define For(i,A,B) for(i=(A);i<=(B);++i) using namespace std; const int N=305; bool s[N]; bitset a[N]; int main(){ int T,n,m,i,j,u,v,w,p,cur; bool ok; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); For(i,1,n)a[i].reset(); while(m--){ scanf("%d%d%d",&u,&v,&w); a[u].flip(u);a[u].set(v); a[v].flip(v);a[v].set(u); if(w){a[u].flip(0);a[v].flip(0);} } cur=1; For(i,1,n){ For(p,cur,n)if(a[p][i])break; if(p>n)continue; if(p!=cur)swap(a[cur],a[p]); For(j,cur+1,n)if(a[j][i])a[j]^=a[cur]; ++cur; } ok=1; For(i,cur,n)if(a[i][0]){ok=0;break;} puts(ok?"yes":"no"); } return 0; }