bzoj4568: [Scoi2016]幸运数字

3 篇文章 0 订阅
1 篇文章 0 订阅

一句话题意:树上两点间路径子集最大异或和。

看了一下线性基发现好强,但我也不太会,先贴代码吧。。

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 20005
#define M 200005
#define Mlg 6000005
#define ll long long
using namespace std;
bool ok[N<<1];
int n,m,x,y,q[M][2];
ll a[N],Ans[M];
int son[N],f[N],fa[N],sum,now,p[M],cnt;
int first[N],next[N<<1],to[N<<1],l=1;
int First[N],Next[Mlg],To[Mlg],L;
ll Re()
{
	ll x=0;char c=getchar();
	while(c<'0'||c>'9')c=getchar();
	while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
	return x;
}
struct B//linear based
{
	ll a[60];
	void clr(){memset(a,0,sizeof a);}
	void add(ll x)
	{
		for (int i=59;i>=0;i--)
			if (x>>i&1){if (a[i])x^=a[i];else{a[i]=x;break;}}
	}
	ll ask()
	{
		ll t=0;
		for (int i=59;i>=0;i--) if ((t^a[i])>t) t^=a[i];
		return t;
	}
}h[N];
void link(int x,int y){to[++l]=y;next[l]=first[x];ok[l]=1;first[x]=l;}
void Link(int x,int y){To[++L]=y;Next[L]=First[x];First[x]=L;}
void Get(int x,int y)
{
	son[x]=1;f[x]=0;
	for (int i=first[x];i;i=next[i])
		if (ok[i]&&to[i]!=y)
		{
			Get(to[i],x);son[x]+=son[to[i]];
			if (son[to[i]]>f[x]) f[x]=son[to[i]];
		}
	f[x]=max(f[x],sum-son[x]);
	if (f[x]<f[now]) now=x;
}
void dfs(int x,int y,int z)
{
	fa[x]=z;
	h[x]=h[y];
	h[x].add(a[x]);
	for (int i=first[x];i;i=next[i])
		if (ok[i]&&to[i]!=y) dfs(to[i],x,z);
}
void work(int x)
{
	if (!First[x]) return;
	f[0]=sum=son[x];
	Get(x,now=0);
	fa[now]=now;
	h[now].clr();
	h[now].add(a[now]);
	for (int i=first[now];i;i=next[i])
		if (ok[i]) dfs(to[i],now,to[i]);
	cnt=0;
	for (int i=First[x];i;i=Next[i])
		p[++cnt]=To[i];
	First[x]=0;
	for (int i=1;i<=cnt;i++)
		if (fa[q[p[i]][0]]==fa[q[p[i]][1]])
			Link(fa[q[p[i]][0]],p[i]);
		else
		{
			B t=h[q[p[i]][0]];
			B*k=h+q[p[i]][1];
			for (int j=59;j>=0;j--)
				if (k->a[j]) t.add(k->a[j]);
			Ans[p[i]]=t.ask();
		}
	for (int i=first[now];i;i=next[i])
		if (ok[i]) ok[i^1]=0,work(to[i]);
}
int main()
{
	n=Re(),m=Re();
	for (int i=1;i<=n;i++)
		a[i]=Re();
	for (int i=1;i<n;i++)
		x=Re(),y=Re(),link(x,y),link(y,x);
	for (int i=1;i<=m;i++)
	{
		q[i][0]=Re(),q[i][1]=Re();
		if (q[i][0]==q[i][1]) Ans[i]=a[q[i][0]];
		else Link(1,i);
	}
	son[1]=n;work(1);
	for (int i=1;i<=m;i++)
		printf("%lld\n",Ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值