DarkBZOJ传送门
解析:
没有注意到会先输入一个测试点编号WA了好几发。。。
其实还是挺好想的。
思路:
首先注意到异或的性质,也就是自反性。
考虑一条边的答案什么时候会被算到。
就是我们选出的点集只包含了它的一个端点。
没选的时候不算,选两个的时候也不算。
很显然我们只需要维护每个点的点权为所有它上面的边的边权异或和就行了。
那么我们要做的就是每次选择一个点集,使得这个点集的异或和最大。
嗯,很裸的线性基。
但是每次都有询问怎么办?不可能每次都暴力构造线性基啊
(不过我试了一下,这样在DarkBZOJ上也能过,可能是数据较水,也可能是机子跑太快了)
考虑每次线性基的修改最多不超过两个节点。也就是说,有相当多的节点是没有变化的。
所以我们可以以时间为下标建立线段树,把每个点的权值相同的时间段放到线段树上,最后来一遍DFS,每个儿子继承父亲的线性基,在叶子节点的时候回答每个询问。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define pc putchar
#define cs const
cs int B=1010,N=502,M=1000;
typedef bitset<B> Base;
inline void print(cs Base &a){
re int i;
for(i=B-1;~i;--i)if(a[i])break;
if(i<0)return pc('0'),(void)pc('\n');
for(int re j=i;~j;--j)pc(a[j]^48);pc('\n');
}
struct Basis{
Base bin[B];
Basis(){}
void insert(Base c){
for(int re i=B-1;~i;--i)
if(c[i]){
if(bin[i].any())c^=bin[i];
else {
bin[i]=c;
return ;
}
}
}
Base query_max(){
Base r;
for(int re i=B-1;~i;--i)
if(!r[i]&&bin[i].any())r^=bin[i];
return r;
}
};
int n,m,last[N];
Base val[N],tmp;
vector<Base> vec[M<<2];
void insert(int k,int l,int r,cs int &ql,cs int &qr,cs Base &val){
if(ql<=l&&r<=qr){vec[k].push_back(val);return ;}
int mid=(l+r)>>1;
if(ql<=mid)insert(k<<1,l,mid,ql,qr,val);
if(mid<qr)insert(k<<1|1,mid+1,r,ql,qr,val);
}
inline void dfs(int k,int l,int r,Basis B){
for(int re i=0;i<vec[k].size();++i)B.insert(vec[k][i]);
if(l==r)return print(B.query_max());
int mid=(l+r)>>1;
dfs(k<<1,l,mid,B);
dfs(k<<1|1,mid+1,r,B);
}
signed main(){
ios::sync_with_stdio(false);
cin>>n;
cin>>n>>m;
for(int re i=1,u,v;i<=m;++i){
static Base tmp;tmp.reset();
cin>>u>>v>>tmp;
if(u==v)continue;
if(last[u])insert(1,1,m,last[u],i-1,val[u]);
last[u]=i,val[u]^=tmp;
if(last[v])insert(1,1,m,last[v],i-1,val[v]);
last[v]=i,val[v]^=tmp;
}
for(int re i=1;i<=n;++i)if(last[i]<=m&&last[i])insert(1,1,m,last[i],m,val[i]);
dfs(1,1,m,Basis());
return 0;
}