2019.01.23【BZOJ4644】经典傻逼题(线段树分治)(线性基)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值