CodeForces 1328E-Tree QueriesLCA

Posted 1024-xzx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CodeForces 1328E-Tree QueriesLCA相关的知识,希望对你有一定的参考价值。

题意:

??给出一棵 (n) 个点的树,(m) 次询问,每次询问给出 (k) 个点,问这 (k) 个点能否在其中某个点到根节点 (1) 的路径上或者与路径的距离为 (1)
数据范围:(2≤n≤2?10^{5}) , (1≤m≤2?10^{5}) , (1≤k_i≤n) , (sum_{i=1}^{m}{k_i}≤2?10^5)

分析:

??首先,要确定路径。显然,应该为深度最深的点到根节点的路径。然后,在判断其他的点是否满足要求。
??一开始的做法是,把第 (i) 询问中的每个点累加到其父亲节点上,然后把路径跑一遍,再剪一下枝,但最后 (t)在了第 (100) 个测试点。
??可能是因为树的形态导致,很可能每次查询的复杂度都是 (O(n))
代码如下:

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef pair<int,int>P;
const int N=2e5+5;
vector<int>pic[N];
int depth[N],par[N],vis[N];
P num[N];
void read(int &x)
{
    x=0;
    int f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch==‘-‘)
            f=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<3)+(x<<1)+ch-‘0‘;
        ch=getchar();
    }
    x*=f;
}
void dfs(int v,int p,int d)
{
    depth[v]=d;
    par[v]=p;
    for(int i=0;i<pic[v].size();i++)
    {
        int u=pic[v][i];
        if(u!=p)
            dfs(u,v,d+1);
    }
}
int main()
{
    int n,m,u,v,k,p;
    read(n),read(m);
    for(int i=1;i<n;i++)
    {
        read(u),read(v);
        pic[u].pb(v);
        pic[v].pb(u);
    }
    dfs(1,0,0);
    for(int i=1;i<=m;i++)
    {
        read(k);
        int maxn=-1,minn=n+1;
        for(int j=1;j<=k;j++)
        {
            read(v);
            vis[v]=i;
            if(depth[v]>maxn)
            {
                maxn=depth[v];
                p=v;
            }
            minn=min(minn,depth[par[v]]);
            if(num[par[v]].first!=i)
            {
                num[par[v]].first=i;
                num[par[v]].second=0;
            }
            num[par[v]].second++;
        }
        int last=n+1;
        while(p)
        {
            if(k==0||depth[p]<minn)
                break;
            if(vis[p]==i)
                k--;
            if(num[p].first==i)
                k-=num[p].second;
            if(vis[last]==i)
               k++;
            last=p;
            p=par[p];
        }
        if(k==0)
            printf("YES
");
        else
            printf("NO
");
    }
    return 0;
}

??因此,要借助 (lca) 来解决。 假设深度最深的点为点 (p) ,对于另一个点 (x) ,要使其满足要求,那么 (lca(p,x)=x),要么 (lca(p,x)=x) 的父亲节点。
??这样的复杂度就比较稳定,为 (O(mlogn))
代码:

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N=2e5+5;
const int mak=20;
vector<int>pic[N];
int depth[N],par[N][mak],vn[N];
void read(int &x)
{
    x=0;
    int f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch==‘-‘)
            f=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<3)+(x<<1)+ch-‘0‘;
        ch=getchar();
    }
    x*=f;
}
void dfs(int v,int p,int d)
{
    depth[v]=d;
    par[v][0]=p;
    for(int i=0;i<pic[v].size();i++)
    {
        int u=pic[v][i];
        if(u!=p)
            dfs(u,v,d+1);
    }
}
void init(int n)
{
    dfs(1,0,0);
    for(int k=0;k+1<mak;k++)
    {
        for(int i=1;i<=n;i++)
            par[i][k+1]=par[par[i][k]][k];
    }
}
int lca(int u,int v)
{
    if(depth[u]>depth[v])
        swap(u,v);
    for(int k=0;k<mak;k++)
    {
        if((depth[v]-depth[u])>>k&1)
            v=par[v][k];
    }
    if(u==v)
        return u;
    for(int k=mak-1;k>=0;k--)
    {
        if(par[u][k]!=par[v][k])
        {
            u=par[u][k];
            v=par[v][k];
        }
    }
    return par[u][0];
}
int main()
{
    int n,m,u,v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        pic[u].pb(v);
        pic[v].pb(u);
    }
    init(n);
    while(m--)
    {
        int k=0,p,maxn=-1;
        scanf("%d",&k);
        for(int i=1;i<=k;i++)
        {
            scanf("%d",&vn[i]);
            if(depth[vn[i]]>maxn)
            {
                maxn=depth[vn[i]];
                p=vn[i];
            }
        }
        bool f=1;
        for(int i=1;i<=k;i++)
        {
            int t=lca(p,vn[i]);
            if(t!=vn[i]&&par[vn[i]][0]!=t)
            {
                f=0;
                break;
            }
        }
        if(f)
            printf("YES
");
        else
            printf("NO
");
    }
    return 0;
}

以上是关于CodeForces 1328E-Tree QueriesLCA的主要内容,如果未能解决你的问题,请参考以下文章

CF1328E-Tree Queries(补) (dfs序)

Codeforces 1328F Make k Equal

=Tree Queries CodeForces - 1328E (LCA+思维)

=Tree Queries CodeForces - 1328E (LCA+思维)

CF-1328 F. Make k Equal

Codeforces Round #629 (Div. 3) E. Tree Queries(lca题)