5589 - Tree

给定一棵树,每条边有一个权值,定义f(u,v)为树链(u,v)的异或和。Q次询问[l,r]中f(u,v)>M的个数。
对于一棵树,有f(u,v)=f(1,u)^f(1,v),然后就转化为一个序列上的问题。
答案用字典树维护。将每个数按数位从高到低插入字典树中(含前缀0)。对于询问就从字典树上往下走,(设这个数为x)每次走x当前位跟m当前位的异或值,如果m的当前位为0,就加上另一个节点的size。
不知道错哪了。欢迎来指正。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
#define rep(i,j,k) for(i=j;i<=k;++i)
#define per(i,j,k) for(i=j;i>=k;--i)
#define sqr(x) ((x)*(x))
#define G getchar()
#define LL long long
#define pll pair<LL,LL>
#define mkp make_pair
#define X first
#define Y second
#define N 100005
#define NN 1600005
struct QUERY{int L,R,wz;}qu[N];int n,m,Q,numm[20];
int tot,he[N],ne[N<<1],to[N<<1],W[N<<1];
int a[N],mo[N],s,t;LL now,ans[N];
int c[2][NN],stk[NN],Tp,mTp;LL cnt[NN];
int read(){
	int x=0;char ch=G;
	for(;ch<48||ch>57;ch=G);
	for(;ch>47&&ch<58;ch=G)x=x*10+ch-48;
	return x;
}
void add(int x,int y,int z){
	to[++tot]=y;ne[tot]=he[x];W[tot]=z;he[x]=tot;
}
void DFS(int x,int e){
	int i,y;
	for(i=he[x];i;i=ne[i])if(i!=e){
		a[y=to[i]]=a[x]^W[i];DFS(y,i^1);
	}
}
bool cmp(QUERY x,QUERY y){
	return mo[x.L]==mo[y.L]?x.R<y.R:mo[x.L]<mo[y.L];
}
int get(){
	int rtn=stk[Tp++];mTp=max(mTp,Tp);
	c[0][rtn]=c[1][rtn]=cnt[rtn]=0;
	return rtn;
}
void ins(int x){
	int i,num[20];
	rep(i,0,19){num[i]=x&1;x>>=1;}x=1;
	per(i,19,0){
		int &y=c[num[i]][x];
		if(!y){y=get();c[0][y]=c[1][y]=0;}
		++cnt[x=y];
	}
}
void del(int x){
	int i,num[20],y;
	rep(i,0,19){num[i]=x&1;x>>=1;}x=1;
	per(i,19,0){
		int &y=c[num[i]][x];x=y;
		if(--cnt[y]==0){stk[--Tp]=y;y=0;}
	}
}
int cal(int x){
	int i,num[20],rtn=0;
	rep(i,0,19){num[i]=x&1;x>>=1;}x=1;
	per(i,19,0){
		if(!x)break;
		if(numm[i])x=c[!num[i]][x];
		else{rtn+=cnt[c[!num[i]][x]];x=c[num[i]][x];}
	}
	return rtn;
}
int main(){
	freopen("r.in","r",stdin);
	freopen("w.out","w",stdout);
	int i,x,y,z,l,r;
	per(i,NN-1,0)stk[i]=i;
	while(~scanf("%d%d%d",&n,&m,&Q)){
		s=ceil(sqrt(n));
		rep(i,1,n)he[i]=0,mo[i]=t+=i%s==1;
		tot=1;
		rep(i,2,n){
			x=read();y=read();z=read();
			add(x,y,z);add(y,x,z);
		}
		DFS(1,0);
		rep(i,1,Q){
			x=read();y=read();qu[i]=(QUERY){x,y,i};
		}
		sort(qu+1,qu+Q+1,cmp);
		per(i,mTp-1,2)stk[i]=i;mTp=Tp=2;c[0][1]=c[1][1]=0;
		ins(a[l=r=qu[1].L]);now=0;
		rep(i,0,19){numm[i]=m&1;m>>=1;}
		rep(i,1,n){
			while(r<qu[i].R){ins(a[++r]);now+=cal(a[r]);}
			while(r>qu[i].R){now-=cal(a[r]);del(a[r--]);}
			while(l>qu[i].L){ins(a[--l]);now+=cal(a[l]);}
			while(l<qu[i].L){now-=cal(a[l]);del(a[l++]);}
			ans[qu[i].wz]=now;
		}
		rep(i,1,Q)printf("%lld\n",ans[i]);
	}
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值