CF825G题解

题目大意

给定一颗有 n n n个点的树,进行 q q q次操作。

操作 1 1 1:将第 x x x个点染为黑色。

操作 2 2 2:询问 x x x到所有黑色点的路径上的编号最小的点。

操作强制在线。

题目思路

先给出做法,在给出证明。

以第一个黑点为根,做一次 d f s dfs dfs,处理出 a i a_i ai表示 i i i到根的路径上编号最小的点。

询问操作的答案就是当前所有激活黑点的 a i a_i ai a x a_x ax取最小。

证明:

设答案点所在路径的两端为 u , v u,v u,v

无论当前路径是否经过根节点, a n s = min ⁡ ( a u , a v ) ans= \min(a_u,a_v) ans=min(au,av)

因为两个点之间的路径可以拆分成 ( u , l c a ( u , v ) ) (u,lca(u,v)) (u,lca(u,v)) ( v , l c a ( u , v ) ) (v,lca(u,v)) (v,lca(u,v))

可以发现这两条路径都会被他们到根的路径包含,又因为每个黑点或询问的点与根的路径都会对答案做出贡献,所以就可以按上面的方法做。

具体实现参考代码。

#include
using namespace std;
int n,q,a[1000000+10],f[1000000+10],root=0,ans=INT_MAX,lastans=0;
vector<int> son[1000000+10];
int read()
{
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
        s=s*10+(ch-'0'),ch=getchar();
    return s*w;
}
void dfs(int u,int fa)
{   
    f[u]=min(u,f[fa]);
    for(auto v:son[u])
    {
        if(v==fa)
            continue;
        dfs(v,u);
    }
}
int main()
{
    n=read(),q=read();
    for(int i=1;i<=n-1;++i)
    {
        int u=read(),v=read();
        son[u].push_back(v);
        son[v].push_back(u);
    }
    for(int i=1;i<=n;++i)
        f[i]=INT_MAX;
    while(q--)
    {
        int opt=read(),u=read();
        u=(u+lastans)%n+1;
        if(opt==1)
        {
            a[u]=1;
            if(!root)
            {
                root=u;
                f[root]=root;
                dfs(root,root);
            }
            ans=min(ans,f[u]);
        }
        else
        {
            lastans=min(ans,f[u]);
            printf("%d\n",lastans);
        }
    }
    return 0;
}

你可能感兴趣的:(题解,算法,深度优先)