tree
Time Limit: 2000/1000 MS (Java/Others)
Memory Limit: 65536/65536 K (Java/Others)
问题描述
有一个树(n个点, n−1条边的联通图),点标号从1~n,树的边权是0或1.求离每个点最近的点个数(包括自己).
输入描述
第一行一个数字T,表示T组数据. 对于每组数据,第一行是一个n,表示点个数,接下来n−1,每行三个整数u,v,w,表示一条边连接的两个点和边权. T=50,1≤n≤100000,1≤u,v≤n,0≤w≤1.
输出描述
对于每组数据,输出答案. 考虑到输出规模过大,设ansi表示第i个点的答案.你只需输出ans1 xor ans2 xor ans3.. xor ansn即可.
输入样例
1 3 1 2 0 2 3 1
输出样例
1
Hint
ans1=2 ans2=2 ans3=1
方法一:自己是利用了两次dfs,第一次dfs求出每个点的子孙到其距离为0的点有多少个,第二次dfs,如果它到其祖先的距离为0,那么到这个点距离为0的点的数目就等于其祖先的数目,否则就是其子孙到其距离为0的数目
方法二:利用距离为0这一点,将树分成一个一个的联通块,每个联通块中的数目就是这个点距离为0的数目,bfs就行
方法三:把每条边权是1的边断开,发现每个点离他最近的点个数就是他所在的连通块大小.
开一个并查集,每次读到边权是0的边就合并.最后Ans_i=size[findset(i)],size表示每个并查集根的size.
#include<bits/stdc++.h> using namespace std; const int maxn=100000+100; int num[maxn]; struct Edge{ int to,next,cost; }e[maxn*2]; int head[maxn],tot; void addedge(int from,int to,int cost){ e[tot].to=to; e[tot].next=head[from]; e[tot].cost=cost; head[from]=tot++; } void init(){ tot=0; memset(head,-1,sizeof(head)); } void dfs(int u,int pre){ num[u]=1; for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to,w=e[i].cost; if(v==pre) continue; dfs(v,u); if(w==0) num[u]+=num[v]; } } void dfs1(int u,int pre){ for(int i=head[u];i!=-1;i=e[i].next){ int v=e[i].to,w=e[i].cost; if(v==pre) continue; if(w==0) num[v]=num[u]; dfs1(v,u); } } int main(){ int _,n; scanf("%d",&_); while(_--){ scanf("%d",&n); init(); int u,v,w; for(int i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } dfs(1,0); dfs1(1,0); int ans=0; for(int i=1;i<=n;i++) ans^=num[i]; printf("%d\n",ans); } return 0; }