BZOJ4644 经典傻逼题
Description
这是一道经典傻逼题,对经典题很熟悉的人也不要激动,希望大家不要傻逼。
考虑一张$N$个点的带权无向图,点的编号为$1$到$N$。 对于图中的任意一个点集(可以为空或者全集),所有恰好有一个端点在这个点集中的边组成的集合被称为割。 一个割的权值被定义为所有在这个割上的边的异或和。一开始这张图是空图, 现在,考虑给这张无向图不断的加边, 加入每条边之后,你都要求出当前权值最大的割的权值, 注意加入的边永远都不会消失。
Solution
如果把点的权值定义为与这个点相连的边的权值的异或,一个点集的割的权值就是这些点的权值的异或
于是问题变成最大异或和
建一棵以时间为下标的线段树,对于每一个点在某个时间段内的权值在线段树上修改
最终计算答案时在线段树上DFS
#include<iostream> #include<cstring> #include<vector> #include<cstdio> #include<bitset> using namespace std; int n,m,tag[505]; char s[1005]; vector<bitset<1005>>ve[4005]; vector<int>bin[4005]; bitset<1005>las[1005],temp; inline int read(){ int f=1,w=0; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return f*w; } void update(int i,int l,int r,int L,int R,int p){ if(L<=l&&r<=R){ve[i].push_back(las[p]);return;} int mid=l+r>>1; if(L<=mid)update(i<<1,l,mid,L,R,p); if(R>mid)update(i<<1|1,mid+1,r,L,R,p); } void insert(int x,int y){ for(int i=1;i<=1000;i++)if(ve[x][y][i]) if(!las[i][i]){las[i]=ve[x][y],bin[x].push_back(i);break;} else ve[x][y]^=las[i]; } void query(){ temp.reset(); for(int i=1;i<=1000;i++)if(!temp[i])temp^=las[i]; int i=1; for(;i<=1000&&!temp[i];i++); if(i==1001)puts("0"); else{ for(;i<=1000;i++)printf("%d",(temp[i]==1)); putchar(10); } } void dfs(int i,int l,int r){ for(int j=0;j<ve[i].size();j++)insert(i,j); if(l==r)query(); else{ int mid=l+r>>1; dfs(i<<1,l,mid),dfs(i<<1|1,mid+1,r); } for(int j=0;j<bin[i].size();j++)las[bin[i][j]].reset(); } int main(){ read(),n=read(),m=read(); for(int i=1;i<=m;i++){ int x=read(),y=read(),len; scanf("%s",s+1),len=strlen(s+1),temp.reset(); if(x==y)continue; for(int j=1;j<=len;j++)temp[1000-len+j]=s[j]-'0'; if(tag[x])update(1,1,m,tag[x],i-1,x); if(tag[y])update(1,1,m,tag[y],i-1,y); tag[x]=tag[y]=i,las[x]^=temp,las[y]^=temp; } for(int i=1;i<=n;i++)if(tag[i]<=m&&tag[i])update(1,1,m,tag[i],m,i),las[i].reset(); dfs(1,1,m); return 0; }