Rainbow Roads(差分前缀和+结点的start 与end 标记)

结点的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;
}

猜你喜欢

转载自blog.csdn.net/xigongdali/article/details/82313517
end