【洛谷P3292】幸运数字

题目

题目链接:https://www.luogu.com.cn/problem/P3292
A 国共有 n 座城市,这些城市由 n-1 条道路相连,使得任意两座城市可以互达,且路径唯一。每座城市都有一个幸运数字,以纪念碑的形式矗立在这座城市的正中心,作为城市的象征。
一些旅行者希望游览 A 国。旅行者计划乘飞机降落在 x 号城市,沿着 x 号城市到 y 号城市之间那条唯一的路径游览,最终从 y 城市起飞离开 A 国。在经过每一座城市时,游览者就会有机会与这座城市的幸运数字拍照,从而将这份幸运保存到自己身上。然而,幸运是不能简单叠加的,这一点游览者也十分清楚。他们迷信着幸运数字是以异或的方式保留在自己身上的。
例如,游览者拍了 3 张照片,幸运值分别是 5,7,11,那么最终保留在自己身上的幸运值就是 9(5 xor 7 xor 11)。
有些聪明的游览者发现,只要选择性地进行拍照,便能获得更大的幸运值。例如在上述三个幸运值中,只选择 5 和 11 ,可以保留的幸运值为 14 。现在,一些游览者找到了聪明的你,希望你帮他们计算出在他们的行程安排中可以保留的最大幸运值是多少。

思路

我们需要把询问的每一条路径在树上对应的线性基求出来。如果直接采用倍增或者树剖,复杂度是 \(O(m\log^3 n)\) 的。
考虑点分治,当我们分到一个点 \(x\) 的时候,dfs 一遍可以得到他到子树内所有点的线性基,然后遍历包含这个点并且没有询问过的所有询问,这样只需要合并两个线性基就可以得到这条路径的线性基。
但是我们不能在每一个点都枚举所有的询问,对每一个点维护一个可能的询问集合 \(S\),考虑当前的根是 \(x\),求出所有询问的 LCA 比并且按照 dfs 序排序,然后就可以根据 dfs 序和 \(x\) 每一个儿子的 dfs 序以及子数大小得出每一个询问下一次要去到哪一个子树中查询。
这样每一个询问只会被考虑 \(O(\log n)\) 次,时间复杂度是 \(O(m\log^2 n)\) 的。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=200010,LG1=60,LG2=20,Inf=1e9;
int n,m,rt,tot,U[N],V[N],P[N],head[N],siz[N],maxp[N],dfn[N],dep[N],f[N][LG2+1];
ll a[N],ans[N];
bool vis[N];
vector<int> qry[N];

struct VectorSpace
{
	ll c[LG1+1];
	
	void ins(ll x)
	{
		for (int i=LG1;i>=0;i--)
			if (x&(1LL<<i))
			{
				if (!c[i]) { c[i]=x; return; }
				x^=c[i];
			}
	}
	
	ll query()
	{
		ll ans=0;
		for (int i=LG1;i>=0;i--)
			if (!(ans&(1LL<<i))) ans^=c[i];
		return ans;
	}
}res,vs[N];

struct edge
{
	int next,to;
}e[N*2];

bool cmp(int x,int y)
{
	return dfn[P[x]]<dfn[P[y]];
}

void add(int from,int to)
{
	e[++tot]=(edge){head[from],to};
	head[from]=tot;
}

void findrt(int x,int fa,int sum)
{
	siz[x]=1; maxp[x]=0;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa && !vis[v])
		{
			findrt(v,x,sum);
			siz[x]+=siz[v];
			maxp[x]=max(maxp[x],siz[v]);
		}
	}
	maxp[x]=max(maxp[x],sum-siz[x]);
	if (maxp[x]<maxp[rt]) rt=x;
}

void dfs(int x,int fa)
{
	dfn[x]=++tot; siz[x]=1;
	f[x][0]=fa; dep[x]=dep[fa]+1;
	for (int i=1;i<=LG2;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	memcpy(vs[x].c,vs[fa].c,sizeof(vs[x].c));
	vs[x].ins(a[x]);
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (v!=fa && !vis[v])
		{
			dfs(v,x);
			siz[x]+=siz[v];
		}
	}
}

int lca(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=LG2;i>=0;i--)
		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
	if (x==y) return x;
	for (int i=LG2;i>=0;i--)
		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	return f[x][0];
}

void calc(int x)
{
	tot=0; dfs(x,0);
	for (int i=0;i<qry[x].size();i++)
		P[qry[x][i]]=lca(U[qry[x][i]],V[qry[x][i]]);
	sort(qry[x].begin(),qry[x].end(),cmp);
	for (int i=0;i<qry[x].size();i++)
		if (P[qry[x][i]]==x)
		{
			int u=U[qry[x][i]],v=V[qry[x][i]];
			memset(res.c,0,sizeof(res.c));
			for (int j=LG1;j>=0;j--)
			{
				if (vs[u].c[j]) res.ins(vs[u].c[j]);
				if (vs[v].c[j]) res.ins(vs[v].c[j]);
			}
			ans[qry[x][i]]=res.query();
		}
		else break;
}

void solve(int x)
{
	calc(x); vis[x]=1;
	int j=0;
	while (j<qry[x].size() && P[qry[x][j]]==x) j++;
	for (int i=head[x];~i;i=e[i].next)
	{
		int v=e[i].to;
		if (!vis[v])
		{
			rt=0; findrt(v,x,siz[v]);
			for (;j<qry[x].size();j++)
				if (dfn[P[qry[x][j]]]<dfn[v]+siz[v])
					qry[rt].push_back(qry[x][j]);
				else break;
			solve(rt);
		}
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	for (int i=1,x,y;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	maxp[0]=Inf; findrt(1,0,n);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&U[i],&V[i]);
		qry[rt].push_back(i);
	}
	solve(rt);
	for (int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;
}
posted @ 2021-03-10 16:26  stoorz  阅读(53)  评论(0编辑  收藏  举报