结点的start 与end 标记:
end 是一个结点的兄弟(该结点也可以有他的孩子)
#include<cstdio>
#include<algorithm>
#include<cstring>
#include <iostream>
#include <vector>
using namespace std;
const int MAXN=300010;
struct Node
{
int to,w;
}q[MAXN];
int dfn;
int pre[MAXN],start[MAXN],endd[MAXN];
int s[MAXN]; //s存的是差分值
vector <Node> G[MAXN];
bool cmp(Node a,Node b)
{
if(a.w!=b.w) return a.w<b.w;
return a.to<b.to;
}
void addedge(int u,int v,int w)
{
Node t={v,w}; G[u].push_back(t);
}
void dfs(int u,int fa)
{
int i;
start[u]=++dfn; //start存的是顺序的位置下标,越后面的结点start就越大。
pre[u]=fa;
for(i=0;i<G[u].size();i++)
if(G[u][i].to!=fa)
dfs(G[u][i].to,u);
endd[u]=dfn; //end存的是该结点所能到达的最后一个结点位置的下标。
} //end加1是它的"兄弟"结点。
int main()
{
int n; int m; int u,v,w; int i,j,k;
scanf("%d",&n);
for(i=1;i<n;i++)
{
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);addedge(v,u,w);
}
int ans=0;
memset(s,0,sizeof(s)); //全都置为0,即假设所有结点都是good点
dfn=0; dfs(1,-1);
k=0;
for(i=1;i<=n;i++) //枚举每个结点 开始的所有路径
{
m=0; //m存的是当前该结点的儿子结点的个数
for(j=0;j<G[i].size();j++) //vector的排序方法
{
q[++m].w=G[i][j].w;
q[m].to=G[i][j].to; //额,这个排序是它儿子结点的排序
}
sort(q+1,q+m+1,cmp);
for(j=1;j<=m;j=k) //下次循环j从第一次出现相同的那个子结点开始
{
k=j+1;
while(k<=m) //统计相同出现的次数
{
if(q[j].w!=q[k].w) break;
else k++;
}
if(k>j+1) //如果出现相同的两段路径,同一个起点的儿子,肯定是相邻的
//考虑相同w路段的情况
{
for(u=j;u<=k-1;u++) //u从第一个相等的那个路径开始到最后一个相等的那个路径
{
v=q[u].to; //找儿子结点的儿子结点
//双向边的儿子 可能儿子是其前驱
/*相同的情况可以分为两种 :
举个例子 :
第一种: 1 2 1
2 3 1
第二种: 1 2 1
1 3 1
*/
if(v==pre[i]) //儿子结点是该结点的前驱结点的情况,也就是第一种情况
{
s[1]++; //如果1结点加1的话,后面的结点都会相继加上1.
s[start[i]]--; //但当前的这个结点是不用计数的,所以要减减,但只是这个结点减而已,
s[endd[i]+1]++; //这个end是辅助数组,前假设么这个end 就是说后面的结点都要减一,但实际是后面的结点是不用减一的。
//在下面的else 要加1就能弥补。。。。。
//这个end加是考虑到第二种的必要才加上的。
}
else //第二种
{
s[start[v]]++; //差分计数
s[endd[v]+1]--; //endd[v]+1就是i的相应的儿子结点。//将该儿子结点下面的所有结点的a值加1
}
}
}
}
}
for(i=1;i<=n;i++) s[i]+=s[i-1]; //差分前缀和
for(i=1;i<=n;i++) if(!s[start[i]]) ans++;
printf("%d\n",ans);
for(i=1;i<=n;i++) if(!s[start[i]]) printf("%d\n",i);
return 0;
}