hdu5589 莫队+字典树

Tree

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 28    Accepted Submission(s): 7


Problem Description
There is a tree, whose vertices are labeled by 1, 2, …, N. They are connected by N–1 edges. Each edge has a weight.For any two vertices u and v, f(u,v) is xor(exclusive-or) sum of weights of all edges on the path from u to v.There are Q queries,for each query,giving you a interval [l,r], you should calculate the number of pairs (u,v) such that f(u,v) > M ( lu<vr ) 。
 


Input
There are multiple test cases.
For each case contains 3 integers N, M and Q on a line. (1N,M,Q50000)
Each of the next N - 1 lines contains three space separated integers a, b and c denoting an edge between a and b, whose weight is c. (1a,bN,0c50000)
Each of the next Q lines contains two integers l and r. (1lrN)
 


Output
For each query,output the answer in a line.
 


Sample Input
  
  
5 10 3 1 2 13 2 3 15 2 4 17 2 5 8 1 5 2 4 3 3
 


Sample Output
  
  
6 3 0
 

求树上(i,j)边权异或和可以转换成i到根的异或和与j到根的异或和的异或和,那么如何求一个数和一些数的异或和大于M的个数呢?我们动态维护一颗字典树,支持插入和删除数字,同时支持查询,利用贪心思想很容易得到个数。如何动态维护当前询问字典树呢?当然考虑莫队了!

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#define Maxn 50010
#define ll long long
using namespace std;

struct edge{
    int to,next,w;
}p[Maxn<<1];
int tot;
int head[Maxn];
int x[Maxn];
void addedge(int u,int v,int w){
    p[tot].to=v;
    p[tot].next=head[u];
    p[tot].w=w;
    head[u]=tot++;
}
void dfs(int u,int fa){
    for(int i=head[u];i!=-1;i=p[i].next){
        int v=p[i].to;
        if(v==fa) continue;
        x[v]=p[i].w^x[u];
        dfs(v,u);
    }
}
int pos[Maxn];
struct query{
    int l,r,id;
    bool operator<(const query &a)const{
        return pos[l]<pos[a.l]||pos[l]==pos[a.l]&&r<a.r;
    }
}q[Maxn];
int sz;
void init(int n,int t){
    sz=(int)sqrt(n);
    for(int i=0;i<n;i++) pos[i]=i/sz;
    sort(q,q+t);
}
int M;
struct trie{
    int ch[Maxn*30][2];
    int val[Maxn*30];
    int sz;
    void init(){
        sz=val[0]=0;
        memset(ch[0],0,sizeof ch[0]);
    }
    void insert(int x){
        int u=0;
        for(int i=17;i>=0;i--){
            int c=x>>i&1;
            if(!ch[u][c]){
                ch[u][c]=++sz;
                val[sz]=0;
                memset(ch[sz],0,sizeof ch[sz]);
            }
            u=ch[u][c];
            val[u]++;
        }
    }
    void del(int x){
        int u=0;
        for(int i=17;i>=0;i--){
            int c=x>>i&1;
            u=ch[u][c];
            val[u]--;
        }
    }
    ll query(int y){
        int u=0;
        ll ans=0;
        for(int i=17;i>=0;i--){
            int c=M>>i&1,d=y>>i&1;
            if(c==1) u=ch[u][d^1];
            else{
                ans+=val[ch[u][d^1]];
                u=ch[u][d];
            }
            if(!u) return ans;
        }
        return ans;
    }
}tr;
ll res[Maxn];
void solve(int t){
    tr.init();
    ll ans=0;
    int l=0,r=-1;
    for(int i=0;i<t;i++){
        while(r<q[i].r) ++r,ans+=tr.query(x[r]),tr.insert(x[r]);
        while(q[i].l<l) --l,ans+=tr.query(x[l]),tr.insert(x[l]);
        while(r>q[i].r) tr.del(x[r]),ans-=tr.query(x[r]),r--;
        while(q[i].l>l) tr.del(x[l]),ans-=tr.query(x[l]),l++;
        res[q[i].id]=ans;
    }
}
int main()
{
    int n,t,u,v,w;
    while(~scanf("%d%d%d",&n,&M,&t)){
        memset(head,-1,sizeof head);
        tot=0;
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&w);
            u--,v--;
            addedge(u,v,w);
            addedge(v,u,w);
        }
        x[0]=0;
        dfs(0,-1);
        for(int i=0;i<t;i++){
            scanf("%d%d",&q[i].l,&q[i].r);
            q[i].l--,q[i].r--,q[i].id=i;
        }
        init(n,t);
        solve(t);
        for(int i=0;i<t;i++)
            printf("%I64d\n",res[i]);
    }
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值