[2021.1.26多校省选模拟9]松鼠串门(线性基/离线策略/贪心)

5 篇文章 0 订阅
3 篇文章 0 订阅

[2021.1.26多校省选模拟9]松鼠串门

询问树上一些父子链的最大异或和.N<=5e5
首先可以想到一个 O ( n l o g n w 2 ) O(nlognw^2) O(nlognw2)的做法,就是倍增维护线性基,每次线性基合并是 O ( w 2 ) O(w^2) O(w2)
然后对于这种父子链我们考虑让儿子继承父亲信息.然后离线询问,采取类似于区间上的离线策略,统计每一个左端点的答案,然后每次移动右端点,这样我们就能够将一些问题贪心处理,让对应贡献在最靠右的位置产生。
但是关键在于深度限制,这里有一个关键思想,就是实际上对于线性基有效的信息并不多,所以我们对于每个基可以贪心地让它的对应深度尽量大,然后我们需要将插入过程进行修改,同时保证对应基的深度尽量大。

#include<bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define V inline void
#define I inline int
#define FOR(i,a,b) for(register int i=a,end##i=b;i<=end##i;++i)
#define REP(i,a,b) for(register int i=a,end##i=b;i>=end##i;--i)
using namespace std;
inline ULL read()
{
	char x='\0';
	ULL fh=1,sum=0;
	for(x=getchar();x<'0'||x>'9';x=getchar())if(x=='-')fh=-1;
	for(;x>='0'&&x<='9';x=getchar())sum=sum*10+x-'0';
	return fh*sum;
}
const int N=1e6+9;
int n,m;
struct liner{
	ULL base[69];
	int dep[69];
	liner(){FOR(i,0,64)base[i]=dep[i]=0;}
	V insert(ULL val,int depth)
	{
		REP(i,64,1)
		{
			if((val>>(i-1))&1)
			{
				if(base[i]==0)
				{
					base[i]=val;
					dep[i]=depth;
					break;
				}
				else if(depth>dep[i])
				{
					ULL tmp=val;
					val^=base[i];
					base[i]=tmp;
					swap(dep[i],depth);
				}
				else val^=base[i];
			}
		}
	}
	ULL que(int depth)
	{
		ULL ans=0;
		REP(i,64,1)if(dep[i]>=depth&&(ans^base[i])>ans)ans^=base[i];
		return ans;
	}
};
struct lian{
	int to,pre;
}e[N<<1];
int hed[N],lcnt=1;
V jlian(int x,int y)
{
	e[++lcnt]={y,hed[x]};
	hed[x]=lcnt;
}
int opt[N];
ULL ai[N];
vector<pair<int,int> >q[N];
ULL ans[N];
int dep[N];
V dfs(int x,int fa,liner now,int depth)
{
	dep[x]=depth;
	now.insert(ai[x],dep[x]);
	for(auto t:q[x])
	{
		int id=t.second,dis=t.first;
		ans[id]=now.que(dep[x]-dis);
	}
	for(int i=hed[x];i;i=e[i].pre)
	{
		int to=e[i].to;
		if(to==fa)continue;
		dfs(to,x,now,depth+1);
	}
}
V file()
{
	freopen("squirrel.in","r",stdin);
	freopen("squirrel.out","w",stdout);
}
int main()
{
	file();
	n=read(),m=read();
//	cout<<n<<' '<<m<<endl;//
	FOR(i,1,n)
	{
		int fa=read();
		ai[i]=read();
		if(fa)jlian(fa,i),jlian(i,fa);
	}
	FOR(i,1,m)
	{
		opt[i]=read();
		ULL u=read(),v=read();
		if(opt[i]==0)q[u].push_back(make_pair(v,i));
		else
		{
			ai[++n]=v;
			jlian(u,n),jlian(n,u);
		}
	}
//	cout<<"!"<<endl;//
	dfs(1,0,liner(),1);
	FOR(i,1,m)if(opt[i]==0)printf("%llu\n",ans[i]);
	return 0;
}
/*
3 4
0 7
1 2
2 4
0 3 1
0 3 2
1 2 1
0 4 1
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值