NOIP 2021全国排位赛 C. 基础图论练习题
【题意】
给定一个n个点m条边的图,每一条边上有一个 0/1 的权值。
定义一条边的邻边为与它有公共顶点的边(显然一条边也是它自己的邻边)。
你每次可以选择一条边,将它所有邻边的权值异或1。
请在 m 次内将所有边的边权变为 0,保证数据有解。
【分析】
我们考虑先确定每一个点的01状态,然后在去处理边的状态。点的0/1状态相当于这个点连接的边有偶数/奇数个被修改了。
下面就对点列一个方程:
将边上的1传递到两个点,这样我们能把依据边的状态列出一个方程,最后看看这个边是否需要操作即可
【代码】
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1005; const int maxm=maxn*maxn; int n,m; vector <int> G[maxn],ans; bitset <maxn> f[maxn]; int g[maxn]; int a[maxm],b[maxm],c[maxm]; int main() { scanf("%d%d",&n,&m); int x,y,z; for(int i=1;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); f[x][y]=f[y][x]=1; f[x].flip(x); f[y].flip(y); a[i]=x; b[i]=y; c[i]=z; if(z) f[x].flip(n+1),f[y].flip(n+1); } for(int i=1;i<=n;i++) f[i].flip(i); for(int i=1;i<=n;i++) { for(int j=i+1;j<=n;j++) if(f[j][i]) { swap(f[j],f[i]); break; } for(int j=i+1;j<=n;j++) if(f[j][i]) f[j]^=f[i]; } for(int i=n;i;i--) { g[i]=f[i][n+1]; for(int j=i-1;j;j--) if(f[j][i]) f[j][n+1]=f[j][n+1]^g[i]; } for(int i=1;i<=m;i++) if(g[a[i]]^g[b[i]]^c[i]) ans.push_back(i); printf("%d\n",ans.size()); for(auto val:ans) printf("%d\n",val); return 0; }