題目連線: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=;
  9. int M[],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<<],w[N<<],v[N<<],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=,int pre=){
  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*][],cnt[N*],ed,c,mc;
  29. void init(){ed=cnt[]=,sum[][]=sum[][]=;}
  30. void insert(int x,int now=){
  31. for(int i=;i>=;i--){
  32. c=x>>i&;
  33. if(!sum[now][c])
  34. sum[now][c]=++ed,cnt[ed]=,sum[ed][]=sum[ed][]=;
  35. now=sum[now][c],cnt[now]++;
  36. }
  37. }
  38. void del(int x,int now=){
  39. for(int i=;i>=;i--)c=x>>i&,now=sum[now][c],cnt[now]--;
  40. }
  41. LL ask(int x,int now=,LL ans=){
  42. for(int i=;i>=;i--){
  43. c=x>>i&,mc=M[i];
  44. if(mc)now=sum[now][c^];
  45. else ans+=cnt[sum[now][c^]],now=sum[now][c];
  46. if(!now)return ans;
  47. }
  48. return ans;
  49. }
  50. }tr;
  51.  
  52. void modui(){
  53. sort(q+,q++k);
  54. int l=,r=;LL ret=;
  55. F(i,,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=;ee<=;ee++)M[ee]=(m>>ee)&;
  68. memset(g,,sizeof(g)),eda=;
  69. F(i,,n-)scanf("%d%d%d",&x,&y,&z),adg(x,y,z),adg(y,x,z);
  70. F(i,,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,,k)printf("%lld\n",ans[i]);
  73. }
  74. return ;
  75. }