[UESTC1284] 郭大侠的苦恼

Description
花开雷霆崖,血染伊吕波。
公元1772年。
郭大侠终于照着天行廖的叮嘱,摆出了阵型,准备带着部队与天行廖相逢于雷霆崖。
但郭大侠实在是没有把握,这个阵型是真的如天行廖所言,所向披靡,于是准备在于天行廖相逢前再确认一遍。
按照天行廖的叮嘱说,这个阵型得满足以下两个要求才能发挥最大威力:
1.这个阵型是一颗树,即n个点,n-1条边,任意两点之间都能互相到达。
2.这个阵型满足好朋友的对数大于n对,好朋友即满足P(u,v)=0,P(u,v)表示从u点到v点的简单路径上点编号的异或和。注意P(u,v)和P(v,u)视作一样,只计数一次。
郭大侠的士兵的的确确摆出了一颗树,但是点数太多了,很难统计好朋友的对数。
这可怎么办?
现在郭大侠找到了你,希望你能帮助憔悴的郭大侠解决这个问题,希望你能够数出这棵树上究竟有多少对好朋友。

Input
第一行n,表示点的数量,(1<=n<=100000)
接下来n-1行,每行两个整数u,v,表示点u与点v之间连有一条边(1<=u,v<=n)

保证是一棵树
Output

输出这棵树上究竟有多少对好朋友。

Sample Input

17
9 8
9 11
9 13
9 15
9 16
11 10
13 12
15 14
16 17
8 1
1 5
5 4
4 6
6 7
7 2
2 3

Sample Output

18

题解:
对于一个dfs,根节点的map维护之前遍历过的子树的每个异或值的个数,然后当前的儿子节点继续往下dfs,在回溯到这里的时候,这个节点上的map就已经维护好了以它为根的各个异或值的个数,接着统计个数就直接相乘就可以了。。
(虽然感觉已经很好了但是100ms的时限是什么玩意?就是卡不过。。)

代码

#include<iostream>
#include<map>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<vector>
#define LiangJiaJun main
using namespace std;
long long res=0;
int n , ne , h[100004];
struct edge{
    int to , next ;
}e[200004];
void insert(int u,int v){
     e[++ne].to = v;
     e[ne].next = h[u];
     h[u] = ne;
}
 map<int , int > A[100004];
 map<int , int >::iterator it;
 void unmap(int x, int y){
      if(A[x].size() < A[y].size())swap(A[x],A[y]);
      while(A[y].size()){
            it = A[y].begin();
            A[x][it->first] += it->second;
            A[y].erase(it);
      }
 }
 void dfs(int x,int FA, int val){
      A[x][val] ++;
      for(int i=h[x];i;i=e[i].next){
          if(e[i].to == FA){
                continue;
          }
          dfs(e[i].to , x , val^e[i].to);
          for(it = A[e[i].to].begin(); it != A[e[i].to].end(); it ++){
              int now = (it->first) ^ x;
              if(A[x].count(now)){
                 res += 1LL * A[x][now] * (it->second);
              }
          }
          unmap(x,e[i].to);
      }
 }
int LiangJiaJun(){
    memset(h,0,sizeof(h));
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        insert(u,v);insert(v,u);
    }
    dfs(1,0,1);
    cout<<res<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值