CF764 C. Timofey and a tree(dfs序+线段树)

博客介绍了如何解决一类树形问题,即在树中寻找一个节点作为根,使得每个子树内所有节点颜色相同。通过参考BZOJ3306题目的解法,博主提出了修改代码的思路,具体方法是对每个节点进行遍历,判断以该节点为根时子树颜色最大值与最小值是否相等,相等则说明找到了符合条件的节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


题目大意:给一棵树,树上每个节点都有颜色,现在要求将某一个点变成根,使得每个子树上的所有点颜色相同,(根的颜色可不管,根的子树与子树间颜色可不同,但同一个 子树颜色必须相同)问能否找到这样的点,若有则输出这个点。


解题思路1:换根,就想到了BZOJ3306,百度了一份代码直接在这上面改。

                   将每一个点都遍历一遍把它变作根,然后遍历它的子树的最大值以及最小值,若两者相等则是符合要求。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0' || ch>'9')
    {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0' && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
#define MAXN 200010
int N,Q,val[MAXN];
struct EdgeNode
{
    int next,to;
} edge[MAXN<<1];
int head[MAXN],cnt;
void AddEdge(int u,int v)
{
    cnt++;
    edge[cnt].next=head[u];
    head[u]=cnt;
    edge[cnt].to=v;
}
void InsertEdge(int u,int v)
{
    if (u==0) return;
    AddEdge(u,v);
    AddEdge(v,u);
}
int pl[MAXN],dfn,pr[MAXN],dfsn[MAXN],deep[MAXN],father[MAXN][21],root;
void DFS(int now,int last)
{
    pl[now]=++dfn;
    dfsn[dfn]=now;
    for (int i=1; i<=20; i++)
        if (deep[now]>=(1<<i))
            father[now][i]=father[father[now][i-1]][i-1];
        else
            break;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=last)
        {
            deep[edge[i].to]=deep[now]+1;
            father[edge[i].to][0]=now;
            DFS(edge[i].to,now);
        }
    pr[now]=dfn;
}
int LCA(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int dd=deep[x]-deep[y];
    for (int i=0; i<=20; i++)
        if (dd&(1<<i)) x=father[x][i];
    for (int i=20; i>=0; i--)
        if (father[x][i]!=father[y][i])
            x=father[x][i],y=father[y][i];
    if (x==y) return x;
    else return father[x][0];
}
struct SegmentTreeNode
{
    int l,r,minn,mx;
} tree[MAXN<<2];
inline void Update(int now)
{
    tree[now].minn=min(tree[now<<1].minn,tree[now<<1|1].minn);
    tree[now].mx=max(tree[now<<1].mx,tree[now<<1|1].mx);
}
void BuildTree(int now,int l,int r)
{
    tree[now].l=l;
    tree[now].r=r;
    if (l==r)
    {
        tree[now].minn=tree[now].mx=val[dfsn[l]];
        return;
    }
    int mid=(l+r)>>1;
    BuildTree(now<<1,l,mid);
    BuildTree(now<<1|1,mid+1,r);
    Update(now);
}

int Query(int now,int L,int R)
{
    if (R<L) return 0x7fffffff;
    int l=tree[now].l,r=tree[now].r;
    if (L==l && R==r) return tree[now].minn;
    int mid=(l+r)>>1,re=0x7fffffff;
    if (R<=mid) return Query(now<<1,L,R);
    else if (L>mid) return Query(now<<1|1,L,R);
    else return min(Query(now<<1,L,mid),Query(now<<1|1,mid+1,R));
    return re;
}
void ChangeRoot(int x)
{
    root=x;
}
int GetAns(int x)
{
    int lca=LCA(root,x);
    if (x==root) return Query(1,1,N);
    if (pl[x]<=pl[root] && pr[x]>=pr[root])
    {
        int dd=deep[root]-deep[x]-1,y=root;
        for (int i=0; i<=20; i++)
            if (dd&(1<<i)) y=father[y][i];
        return min(Query(1,1,pl[y]-1),Query(1,pr[y]+1,dfn));
    }
    return Query(1,pl[x],pr[x]);
}

int Query1(int now,int L,int R)
{
    if (R<L) return -1;
    int l=tree[now].l,r=tree[now].r;
    if (L==l && R==r) return tree[now].mx;
    int mid=(l+r)>>1,re=-1;
    if (R<=mid) return Query1(now<<1,L,R);
    else if (L>mid) return Query1(now<<1|1,L,R);
    else return max(Query1(now<<1,L,mid),Query1(now<<1|1,mid+1,R));
    return re;
}

int GetAns1(int x)
{
    int lca=LCA(root,x);
    if (x==root) return Query1(1,1,N);
    if (pl[x]<=pl[root] && pr[x]>=pr[root])
    {
        int dd=deep[root]-deep[x]-1,y=root;
        for (int i=0; i<=20; i++)
            if (dd&(1<<i)) y=father[y][i];
        return max(Query1(1,1,pl[y]-1),Query1(1,pr[y]+1,dfn));
    }
    return Query1(1,pl[x],pr[x]);
}

int main()
{
    N=read();
    for (int fa,son,i=1; i<N; i++) fa=read(),son = read(),InsertEdge(fa,son);
    for(int i=1; i<=N; i++) val[i]=read();
    DFS(1,0);
    root=1;
    BuildTree(1,1,dfn);
    //int flag = 0;
    int flag;
    for(int i=1; i<=N; i++)
    {
        int x = i;
        ChangeRoot(x);
        flag = 1;
        for(int i=head[x]; i; i=edge[i].next)
        {
            int v = edge[i].to;
            if(v==x)continue;
            int a = GetAns(v);
            //a = min(a,val[v]);
            int b = GetAns1(v);
            //b = max(b,val[v]);
            if(a!=b)
            {
                flag = 0;
                break;
            }
            //printf("%d %d %d\n",x,a,b);
        }
        if(flag)
        {
            printf("YES\n%d\n",x);
            break;
        }
    }
    if(!flag)
    {
        printf("NO\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值