zoukankan      html  css  js  c++  java
  • hdu_5589_Tree(莫队+字典树)

    题目连接:hdu_5589_Tree

    题意:给你一棵树和一些边值,n个点n-1条边,一个m,q个询问,每个询问让你输出在[l,r]区间内任意两点树上的路径的边权异或的和大于m的点对数。

    题解:这题很巧妙,看数据知道要用莫队,不过如何来处理树上任意两点的边权异或和大于m呢?我们知道,一个数和另一个数异或两次等于自己,如果我们记录所有的点都与1这个点的路径异或和,不就可以得出任意两点的路径异或和了吗,然后如果我们要用莫队,就要找到增加,删除的时候答案对应的变化,要支持增加删除,并且要找比m大的异或值,01字典树是一个不错的选择,我们考虑如果要找比m大的数,那么在二进制下,前面的位肯定都相同,后面的某一位m为0,当前数为1才有比m大,我们在将异或和插入字典树的时候,转换为二进制,从高位开始插,每插一位,当前的cnt++,表示在当前位为0或者1的数有一个,删除的时候就对应cnt--就行了。

    询问:这里设即将插入的数为节点x到1的节点的异或值为sum,我们要和m相比,因为要找比m大的数,而我们此时插入的都是当前节点到根节点的异或和,这里我们就要用到贪心的思想,从高位开始找,当m的当前位为1时,此时你只能找字典树中为与sum当前位异或为1的,如果不找与sum当前位异或为1的那你后面的位无论怎么找,都不能大于m,要与sum当前位异或为1,当sum的当前位为0,应找1这个子节点,当sum当前位为1,应找0这个子节点,所以就是当m的当前位为1时,下一个子节点应为(sum的当前位^1),当m的当前位为0时,直接加上与sum当前位异或为1的子节点的cnt,因为到这一位的时候,与sum当前位异或为1,那后面位与sum异或完后必然是大于m的,所以直接加上当前与sum异或为1的子节点的cnt就行了,然后我们继续搜寻与sum当前为异或为0的下一位,和上面一样,要使与sum当前位异或为0,sum当前位^0=sum当前位。

    最后莫队处理完就是结果了

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<cmath>
     4 #include<algorithm>
     5 #define F(i,a,b) for(int i=a;i<=b;i++)
     6 using namespace std;
     7 typedef long long LL;
     8 const int N=50011;
     9 int M[20],va[N],sqr,n,m,k,x,y,z;
    10 LL ans[N];
    11 struct query{
    12     int l,r,id,sq;
    13     bool operator<(const query & b)const{
    14         if(sq==b.sq)return r<b.r;
    15         return sq<b.sq;
    16     }
    17 }q[N];
    18 //-------------------------树的处理-----------------
    19 int g[N],nxt[N<<1],w[N<<1],v[N<<1],eda;
    20 inline void adg(int x,int y,int z){v[++eda]=y,w[eda]=z,nxt[eda]=g[x],g[x]=eda;}
    21 
    22 void dfs(int u=1,int pre=0){
    23     for(int i=g[u];i;i=nxt[i])
    24         if(v[i]!=pre)va[v[i]]=w[i]^va[u],dfs(v[i],u);
    25 }
    26 //----------------字典树----------------------------
    27 struct Trie{
    28     int sum[N*20][2],cnt[N*20],ed,c,mc;
    29     void init(){ed=cnt[0]=0,sum[0][0]=sum[0][1]=0;}
    30     void insert(int x,int now=0){
    31         for(int i=17;i>=0;i--){
    32             c=x>>i&1;
    33             if(!sum[now][c])
    34             sum[now][c]=++ed,cnt[ed]=0,sum[ed][0]=sum[ed][1]=0;
    35             now=sum[now][c],cnt[now]++;
    36         }
    37     }
    38     void del(int x,int now=0){
    39         for(int i=17;i>=0;i--)c=x>>i&1,now=sum[now][c],cnt[now]--;
    40     }
    41     LL ask(int x,int now=0,LL ans=0){
    42         for(int i=17;i>=0;i--){
    43             c=x>>i&1,mc=M[i];
    44             if(mc)now=sum[now][c^1];
    45             else ans+=cnt[sum[now][c^1]],now=sum[now][c];
    46             if(!now)return ans;
    47         }
    48         return ans;
    49     }
    50 }tr;
    51 
    52 void modui(){
    53     sort(q+1,q+1+k);
    54     int l=1,r=0;LL ret=0;
    55     F(i,1,k){
    56         while(r<q[i].r)r++,ret+=tr.ask(va[r]),tr.insert(va[r]);
    57         while(l>q[i].l)l--,ret+=tr.ask(va[l]),tr.insert(va[l]);
    58         while(r>q[i].r)tr.del(va[r]),ret-=tr.ask(va[r]),r--;
    59         while(l<q[i].l)tr.del(va[l]),ret-=tr.ask(va[l]),l++;
    60         ans[q[i].id]=ret;
    61     }
    62 }
    63 
    64 int main(){
    65     while(~scanf("%d%d%d",&n,&m,&k)){
    66         sqr=(int)sqrt(n);
    67         for(int ee=0;ee<=17;ee++)M[ee]=(m>>ee)&1;
    68         memset(g,0,sizeof(g)),eda=0;
    69         F(i,1,n-1)scanf("%d%d%d",&x,&y,&z),adg(x,y,z),adg(y,x,z);
    70         F(i,1,k)scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i,q[i].sq=q[i].l/sqr;
    71         dfs(),tr.init(),modui();
    72         F(i,1,k)printf("%lld
    ",ans[i]);
    73     }
    74     return 0;
    75 }
    View Code


  • 相关阅读:
    Two strings CodeForces
    Dasha and Photos CodeForces
    Largest Beautiful Number CodeForces
    Timetable CodeForces
    Financiers Game CodeForces
    AC日记——整理药名 openjudge 1.7 15
    AC日记——大小写字母互换 openjudge 1.7 14
    AC日记——将字符串中的小写字母换成大写字母 openjudge 1.7 13
    AC日记——加密的病历单 openjudge 1.7 12
    AC日记——潜伏着 openjudge 1.7 11
  • 原文地址:https://www.cnblogs.com/bin-gege/p/5696091.html
Copyright © 2011-2022 走看看