CodeForces 1328E-Tree Queries【LCA】

题意:

  给出一棵 \(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\n");
        else
            printf("NO\n");
    }
    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\n");
        else
            printf("NO\n");
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/1024-xzx/p/12580534.html