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;
}

原文地址:https://www.cnblogs.com/1024-xzx/p/12580534.html

时间: 2024-08-28 05:10:25

CodeForces 1328E-Tree Queries【LCA】的相关文章

[CodeForces - 1225D]Power Products 【数论】 【分解质因数】

[CodeForces - 1225D]Power Products [数论] [分解质因数] 题目描述 Time limit 2000 ms Memory limit 524288 kB Source Technocup 2020 - Elimination Round 2 Tags hashing math number theory *1900 Site https://codeforces.com/problemset/problem/1225/D 题面 Example Input 6

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

https://codeforces.com/contest/1328/problem/E E. Tree Queries You are given a rooted tree consisting of nn vertices numbered from 11 to nn. The root of the tree is a vertex number 11. A tree is a connected undirected graph with n−1n−1 edges. You are

【LCA】BZOJ1776-[Usaco2010 Hol]cowpol 奶牛政坛

[题目大意] 一棵n个点的树,树上每个点属于一个党派,要求每个党派的最远距离点.两点间距离为两点间边的个数. [思路] yy一下可知,最远距离点中必有一个是该党派深度最深的一个,那么我们就记下最深的点,然后枚举跑LCA--O(nlongn)裸的倍增LCA. 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=200000+50; 4 const int DEG=20; 5 int n,k,rt; 6 int dep

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

https://codeforces.com/contest/1328/problem/E 题目所描述的是一棵树,题中已明示1为root结点. 题目可以转化为,是否存在一条路径,满足集合中的k个点到路径的距离小于等于1? 思路: 1.首先倍增离线预处理出结点深度,便于后续在线询问LCA 2.对于每次的询问,依次扫描k个点.对于集合中的u和v两点,每次我们求出u和v的LCA,计算u和v到LCA的距离,如果u和v到LCA的距离同时大于1,那么说明无法找到一条路径,使得u和v到该路径链的距离小于等于1

【Tarjan】【LCA】【动态规划】【推导】hdu6065 RXD, tree and sequence

划分出来的每个区间的答案,其实就是连续两个的lca的最小值. 即5 2 3 4 这个区间的答案是min(dep(lca(5,2)),dep(lca(2,3),dep(lca(3,4)))). 于是dp即可,f(i,j)表示前i个数,划分成j段的最优值. 只有三种决策,要么不取,继承f(i-1,j),要么将其自己作为某段的最小值,转移自f(i-1,j-1),要么将其与其前位的lca作为某段的最小值,转移自f(i-2,j-1). 如果用tarjan预处理相邻的lca的话,复杂度是O(n*K). 比s

【树链剖分】【dfs序】【LCA】【分类讨论】Codeforces Round #425 (Div. 2) D. Misha, Grisha and Underground

一棵树,q次询问,每次给你三个点a b c,让你把它们选做s f t,问你把s到f +1后,询问f到t的和,然后可能的最大值是多少. 最无脑的想法是链剖线段树--但是会TLE. LCT一样无脑,但是少一个log,可以过. 正解是分类讨论, 如果t不在lca(s,f)的子树内,答案是dis(lca(s,f),f). 如果t在lca(s,f)的子树内,并且dep(lca(s,t))>dep(lca(f,t)),答案是dis(lca(s,t),f): 否则答案是dis(lca(f,t),f). #in

poj 1330 Nearest Common Ancestors 【LCA】

Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20073   Accepted: 10631 Description A rooted tree is a well-known data structure in computer science and engineering. An example is shown below: In the figure, each

Hdoj 2586 How far away ? 【LCA】

How far away ? Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 7072 Accepted Submission(s): 2575 Problem Description There are n houses in the village and some bidirectional roads connecting them.

poj 3417 Network 【LCA】【树中增新边后 求每条树边被环所覆盖的次数】

Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4251   Accepted: 1223 Description Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has just received a bad news which denotes that DxtNet