zoukankan      html  css  js  c++  java
  • 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
    ");
            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;
    }
    
    
  • 相关阅读:
    第四周作业
    jsp第二次作业
    jsp第一次作业
    软件测试1
    activity
    listview
    sql
    登录
    第二次安卓作业
    安卓第一周作业
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/12580534.html
Copyright © 2011-2022 走看看