CF1055F Tree and XOR

题面传送门
这道题卡空间卡到了丧心病狂的程度。
两点间路径可以转化为前缀异或值。
对于一般的这样的题目,我们可以直接在一棵 t r i e trie trie上分治,每次选更大的一边,时空复杂度都是 O ( n l o g w ) O(nlogw) O(nlogw)
但是这道题卡空间,所以只能用滚动 t r i e trie trie
何为滚动 t r i e trie trie?
可以发现,这道题在某种意义上只有一次查询,即只遍历一遍这颗 t r i e trie trie,所以我们只要保存上一层的节点即可。
代码实现:

#include<cstdio>
#include<cstring>
using namespace std;
int n,m,d,tot,now,last,p[1000039],pl[1000039];
long long q[1000039],k,x,y,z,ans,pus;
struct yyy{int to;long long w;int z;};
struct ljb{
	int head,h[1000039];
	yyy f[2000039];
	inline void add(int x,int y,long long z){
		f[++head]=(yyy){y,z,h[x]};
		h[x]=head;
	}
}s;
struct tree{int l,r,f;}f[2][1000039];
static char buf[1000000],*p1=buf,*p2=buf;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++
inline void read(long long &x){
	char s=getchar();x=0;
	while(s<'0'||s>'9') s=getchar();
	while(s>='0'&&s<='9') x=x*10+s-48,s=getchar();
}
inline void dfs(int x,int last){
	int cur=s.h[x];
	yyy tmp;
	while(cur!=-1){
		tmp=s.f[cur];
		if(tmp.to!=last) q[tmp.to]=q[x]^tmp.w,dfs(tmp.to,x);
		cur=tmp.z;
	}
}
int main(){
//	freopen("1.in","r",stdin);
	memset(s.h,-1,sizeof(s.h));
	register int i;
	scanf("%d%lld",&n,&k);k=(long long)n*n+1-k;
	for(i=2;i<=n;i++) read(x),read(z),s.add(x,i,z),s.add(i,x,z);
	dfs(1,0);
	d=62;
	for(i=1;i<=n;i++) p[i]=1,pl[i]=1;
	while(d!=-1){
		last=now;now=last^1;
		tot=0;
		for(i=1;i<=n;i++) f[now][i].l=f[now][i].r=f[now][i].f=0;
		for(i=1;i<=n;i++){
			if(!(q[i]&(1ll<<d))) {
				if(!f[last][p[i]].l) f[last][p[i]].l=++tot;
				p[i]=f[last][p[i]].l;f[now][p[i]].f++;
			}
			else{
				if(!f[last][p[i]].r) f[last][p[i]].r=++tot;
				p[i]=f[last][p[i]].r;f[now][p[i]].f++;
			}
		} 
		ans=0;
		for(i=1;i<=n;i++){
			if(q[i]&(1ll<<d))ans+=f[now][f[last][pl[i]].l].f;
			else ans+=f[now][f[last][pl[i]].r].f;
		}
	//	printf("%d %lld %lld\n",d,ans,k);
		if(ans>=k){
			pus^=(1ll<<d);
			for(i=1;i<=n;i++){
			    if(q[i]&(1ll<<d))pl[i]=f[last][pl[i]].l;
		    	else pl[i]=f[last][pl[i]].r;
	    	}
		}
		else{
			k-=ans;
			for(i=1;i<=n;i++){
			    if(q[i]&(1ll<<d))pl[i]=f[last][pl[i]].r;
		    	else pl[i]=f[last][pl[i]].l;
	    	}
		}
		d--;
	}
	printf("%lld\n",pus);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值