正题
题目链接:https://loj.ac/p/3130
题目大意
给出nnn个点mmm条边的一张有权无向图,你每次可以选择一个边集异或上一个值,要求最少次数使得所有简单环异或和都为000。
1≤n,m≤1051\leq n,m\leq 10^51≤n,m≤105
解题思路
先找一棵生成树,然后每条非树边都会产生一个简单环,显然这些简单环合法了其他的也一定合法。
而肯定存在一种最优的方案是只改非树边,因为如果该树边首先我们可以一次改一个集合所有必须改一条树边会对多个简单环产生不同的影响,而如果我们异或的是valvalval,那么我们能做到的只是让某个简单环异或上valvalval(奇数条树边操作),或者不异或上valvalval(偶数条树边操作),所以是和我们操作非树边能做到的是相同的。
那么问题就变为已知一些数要异或多少,求最少操作次数了,我们用线性基求出最小的线性空间就好了。
时间复杂度:O(n+mlogm)O(n+m\log m)O(n+mlogm)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define mp(x,y) make_pair(x,y)
using namespace std;
const int N=1e5+10;
struct node{int to,next,w;
}a[N<<1];
int n,m,tot,k,w[N],d[N],ls[N],v[N];
bool vis[N];
void addl(int x,int y,int w){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w;return;
}
void dfs(int x,int fa){vis[x]=1;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa)continue;if(vis[y]){if(i&1)v[(i+1)/2]=w[x]^w[y]^a[i].w;}else{w[y]=w[x]^a[i].w;dfs(y,x);}}return;
}
void ins(int x){for(int i=29;i>=0;i--)if((x>>i)&1){if(d[i])x^=d[i];else {d[i]=x;k+=(x!=0);break;}}return;
}
int main()
{scanf("%d%d",&n,&m);for(int i=1,x,y,w;i<=m;i++){scanf("%d%d%d",&x,&y,&w);addl(x,y,w);addl(y,x,w);}dfs(1,0);for(int i=1;i<=m;i++)ins(v[i]);printf("%d\n",k);for(int i=0;i<=29;i++)if(d[i]){int cnt=0;for(int j=1;j<=m;j++)cnt+=((v[j]>>i)&1);printf("%d %d ",d[i],cnt);for(int j=1;j<=m;j++)if((v[j]>>i)&1)printf("%d ",j);putchar('\n');}return 0;
}