UOJ513 清扫银河(高斯消元)

 首先转化一波:

对于1操作,我们求原图的生成树,然后对于每条非树边记下它和树边形成的环,这样的操作共有 $m-n+1$ 个;

对于2操作,我们考虑对于 $n$ 个结点,把和这个结点相连的边状态反转。

 不难发现任何操作序列都可以拆成这 $m+1$ 个操作,且显然每个操作最多进行一次,所以只用考虑这 $m+1$ 个操作就可以了。

直接跑异或方程组消元就可以做到 $O(\frac{Tm^3}{w})$。

怎么优化呢?手玩一下可以发现:如果和每个点相连的堵塞边条数都是偶数,那么一定有解(证明可以看官方题解反正我也不会证

而1操作是不会改变这个值的奇偶性的,因此只用考虑2操作,复杂度下降为 $O(\frac{Tn^3}{w})$。

一个简单的 trick:跑高斯消元的时候把所有全零式子扔到最后,那么判有解/无解只要检查最后的式子即可。

#include<cstdio>
#include<algorithm>
#include<bitset>
#define For(i,A,B) for(i=(A);i<=(B);++i)
using namespace std;
const int N=305;
bool s[N];
bitset<N> 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;
}
View Code

 

posted @ 2020-04-06 10:36  wangyuchen  阅读(219)  评论(0编辑  收藏  举报