其他分享
首页 > 其他分享> > P8201 题解

P8201 题解

作者:互联网

题意:

给定一棵树,定义 \(d(u,v)\) 为 \(u\) 和 \(v\) 路径上所有点权的异或,多组询问,给定 \(u,v,k\),问是否存在 \(x\) 使得 \(d(u,x) \bigoplus d(x,v)=k\)。

思路:

观察性质发现,如果存在 \(x\) 不在 \(u,v\) 路径上满足条件,那么 \(u,x\) 路径和 \(v,x\) 路径的第一个交点必然也满足条件,因为第一个交点往后必然一直重合,所以都能够抵消。还可以发现,这个交点必然在 \(u,v\) 路径上,想一想,为什么。

因此,问题等价于在 \(u,v\) 路径上是否存在 \(x\) 使得 \(d(u,x) \bigoplus d(x,v)=k\),由于 \(d(u,x) \bigoplus d(x,v)=d(u,v)\bigoplus w_x\),转化为在 \(u,v\) 路径上是否存在 \(x\) 使得 \(d(u,v)\bigoplus w_x=k\),左右同时异或 \(d(u,v)\) 得到 \(w_x=d(u,v)\bigoplus k\),由于右式是个常数,设为 \(y\),最终转化为判断一条路径上是否存在点权为 \(y\) 的点。

与其维护是否存在,不如直接维护个数,定义 \(f(u,v,w)\) 表示 \(u,v\) 路径上 \(x\) 的出现次数。

由于出现次数具有可减性,所以 \(f(u,v,w)=f(u,1,w)+f(v,1,w)-f(lca(u,v),1,w)-f(father_{lca(u,v)},1,w)\),把 \(f(u,v,w)\) 这一个询问拆成四个询问处理,转化为任意 \(u,w\) 求 \(f(u,1,w)\)。

这很明显是个数据结构问题,下面给出两种做法:

  1. 考虑主席树,由于加入 \(x\) 这个点相当于是在其父亲的版本上加入有关 \(w_x\) 的信息,那么可以在合理的时间内建出每个点的主席树,直接求解即可,代码难度稍大。
  2. 考虑离线下来,全局桶维护,离开这个点时删除这个点在桶中的贡献,那么对于每一个点都能得到一个维护它到根路径信息的桶,但是这个点的顺序是 dfn 序,对询问的点并不连续,所以将所有拆开的询问按 dfn 排序,那么可以得到答案的总是询问的一个前缀,单指针扫一遍即可,实现细节比较多。

代码:

给出第二种做法的代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,id,dfn[(int)(5e5+10)],w[(int)(5e5+10)],f[35][(int)(5e5+10)];
int res[(int)(5e5+10)],dep[(int)(5e5+10)],p[(int)(5e5+10)],d[(int)(5e5+10)];
vector<int>edge[(int)(5e5+10)];
void dfs(int u,int fa)
{
	dfn[u]=++id;f[0][u]=fa;
	dep[u]=dep[fa]+1;p[u]^=p[fa];
	for(int i=1;i<=20;i++)
	{
		f[i][u]=f[i-1][f[i-1][u]];
	}
	for(int i=0;i<edge[u].size();i++)
	{
		int v=edge[u][i];
		if(v==fa) continue;
		dfs(v,u);
	}
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    while(dep[x]>dep[y])
    {
        x=f[d[dep[x]-dep[y]]-1][x];
    }
    if(x==y) return x;
    for(int i=d[dep[x]]-1;i>=0;i--)
    {
        if(f[i][x]!=f[i][y]) x=f[i][x],y=f[i][y];
    }
    return f[0][x];
}

struct node
{
	int id,type,u,val,dfn,from,ans;
}a[(int)(2e6+10)],b[(int)(2e6+10)];
bool cmp(node a,node b)
{
	return a.dfn<b.dfn;
}
map<int,int>mp;
int now=1,top=0;
void dfs1(int u,int fa)
{
	mp[w[u]]++;
	while(a[now].u==u&&now<=top) 
	{
		a[now].ans=mp[a[now].val];
		now++;
	}
	for(int i=0;i<edge[u].size();i++)
	{
		int v=edge[u][i];
		if(v==fa) continue;
		dfs1(v,u);
	}
	mp[w[u]]--;
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++) scanf("%lld",&w[i]),p[i]=w[i];
	for(int i=1;i<n;i++)
	{
		int u,v;scanf("%lld %lld",&u,&v);
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	for(int i=1;i<=n;i++)
    {
        d[i]=d[i-1]+(1<<d[i-1]==i);
    }
	dfs(1,0);
	for(int i=1;i<=m;i++)
	{
		int u,v,k;scanf("%lld %lld %lld",&u,&v,&k);
		int t=lca(u,v);
		if(t==u||t==v)
		{
			if(v==t)
			{
				a[++top]=(node){top,1,u,p[u]^p[f[0][t]]^k,dfn[u],i,0};
				if(t!=1) a[++top]=(node){top,-1,f[0][t],p[u]^p[f[0][t]]^k,dfn[f[0][t]],i,0};
			}
			else
			{
				a[++top]=(node){top,1,v,p[v]^p[f[0][t]]^k,dfn[v],i,0};
				if(t!=1) a[++top]=(node){top,-1,f[0][t],p[v]^p[f[0][t]]^k,dfn[f[0][t]],i,0};
			}
		}
		else
		{
			a[++top]=(node){top,1,u,p[u]^p[v]^w[t]^k,dfn[u],i,0};
			a[++top]=(node){top,1,v,p[u]^p[v]^w[t]^k,dfn[v],i,0};
			a[++top]=(node){top,-1,t,p[u]^p[v]^w[t]^k,dfn[t],i,0};
			if(t!=1) a[++top]=(node){top,-1,f[0][t],p[u]^p[v]^w[t]^k,dfn[f[0][t]],i,0};
		}
	}
	sort(a+1,a+1+top,cmp);
	dfs1(1,0);
	for(int i=1;i<=top;i++)
	{
		// cout<<a[i].u<<"\n";
		b[a[i].id]=a[i];
	}
	for(int i=1;i<=top;i++)
	{
		res[b[i].from]+=b[i].type*b[i].ans;
	}
	for(int i=1;i<=m;i++)
	{
		// assert(res[i]>=0);
		if(res[i]) puts("Yes");
		else puts("No");
	}
	system("pause > null");
}

标签:10,int,题解,路径,dep,bigoplus,5e5,P8201
来源: https://www.cnblogs.com/blog-WHY/p/16220719.html