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;
}