NOIP 2021全国排位赛 C. 基础图论练习题

【题意】

给定一个nm条边的图,每一条边上有一个 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;
}

 

posted @ 2021-11-18 21:55  andyc_03  阅读(49)  评论(0编辑  收藏  举报