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

E. Tree Queries

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

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 given mm queries. The ii-th query consists of the set of kiki distinct vertices vi[1],vi[2],…,vi[ki]vi[1],vi[2],…,vi[ki]. Your task is to say if there is a path from the root to some vertex uu such that each of the given kk vertices is either belongs to this path or has the distance 11 to some vertex of this path.

Input

The first line of the input contains two integers nn and mm (2≤n≤2⋅1052≤n≤2⋅105, 1≤m≤2⋅1051≤m≤2⋅105) — the number of vertices in the tree and the number of queries.

Each of the next n−1n−1 lines describes an edge of the tree. Edge ii is denoted by two integers uiui and vivi, the labels of vertices it connects (1≤ui,vi≤n,ui≠vi(1≤ui,vi≤n,ui≠vi).

It is guaranteed that the given edges form a tree.

The next mm lines describe queries. The ii-th line describes the ii-th query and starts with the integer kiki (1≤ki≤n1≤ki≤n) — the number of vertices in the current query. Then kiki integers follow: vi[1],vi[2],…,vi[ki]vi[1],vi[2],…,vi[ki] (1≤vi[j]≤n1≤vi[j]≤n), where vi[j]vi[j] is the jj-th vertex of the ii-th query.

It is guaranteed that all vertices in a single query are distinct.

It is guaranteed that the sum of kiki does not exceed 2⋅1052⋅105 (∑i=1mki≤2⋅105∑i=1mki≤2⋅105).

Output

For each query, print the answer — "YES", if there is a path from the root to some vertex uu such that each of the given kk vertices is either belongs to this path or has the distance 11 to some vertex of this path and "NO" otherwise.

Example

input

Copy

10 6
1 2
1 3
1 4
2 5
2 6
3 7
7 8
7 9
9 10
4 3 8 9 10
3 2 4 6
3 2 1 5
3 4 8 2
2 6 10
3 5 4 7

output

Copy

YES
YES
YES
YES
NO
NO

Note

The picture corresponding to the example:

e76bd5b034f329b9a83adf0b8731c659d4d0c096.pnguploading.4e448015.gif正在上传…重新上传取消

Consider the queries.

The first query is [3,8,9,10][3,8,9,10]. The answer is "YES" as you can choose the path from the root 11 to the vertex u=10u=10. Then vertices [3,9,10][3,9,10] belong to the path from 11 to 1010 and the vertex 88 has distance 11 to the vertex 77 which also belongs to this path.

The second query is [2,4,6][2,4,6]. The answer is "YES" as you can choose the path to the vertex u=2u=2. Then the vertex 44 has distance 11 to the vertex 11 which belongs to this path and the vertex 66 has distance 11 to the vertex 22 which belongs to this path.

The third query is [2,1,5][2,1,5]. The answer is "YES" as you can choose the path to the vertex u=5u=5 and all vertices of the query belong to this path.

The fourth query is [4,8,2][4,8,2]. The answer is "YES" as you can choose the path to the vertex u=9u=9 so vertices 22 and 44 both have distance 11 to the vertex 11 which belongs to this path and the vertex 88 has distance 11 to the vertex 77 which belongs to this path.

The fifth and the sixth queries both have answer "NO" because you cannot choose suitable vertex uu.

 

题意:给你一颗n(<=2e5)个节点的无向无权树,1号节点为根,q(<=2e5)次询问,接下来n-1条边给你,再接下来q行,每行第一个数s表示本次询问有s个点,然后依次给你s个节点,问是否存在一个点u,使得这s个点,要么位于1到u的路径上,要么与1到u的路径上某个点距离为1。

思路:考虑使用lca。对于每次询问,我们维护必须在路径上的点的端点u(另一个端点是1)。

设当前输入的点为x,

① 如果这个点是根节点或者是根节点的儿子节点,那么忽略。

② 否则若u不存在,置为father[x]。

③如果u存在,x或father[x]位于位于1-u的路径上,那么忽略。否则如果lca(u,x)==u,则置u为fa[x]。(注意这两个顺序不能颠倒)否则路径不存在。

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define dep(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int maxn=400010;
int n,m,q,tot,tot2,cnt,tmp,ans;
int f[maxn][20];
int head[maxn];
ll dis[maxn];
int dfn[maxn],d[maxn];
int dep[maxn],e[maxn],pos[maxn],fa[maxn];
bool vis[maxn];
struct node
{
    int to,nex;
    ll w;
}a[maxn];
void add(int u,int v,ll w)
{
    a[cnt].to=v;
    a[cnt].w=w;
    a[cnt].nex=head[u];
    head[u]=cnt++;
}
void init()
{
    cnt=tot=tot2=0;
    memset(head,-1,sizeof(head));
    memset(pos,-1,sizeof(pos));
    memset(vis,0,sizeof(vis));
    dis[1]=0;
}
void dfs(int u,int deep)//1
{
    if(pos[u]!=-1)return;
    dfn[tot2]=u;d[u]=tot2++;
    pos[u]=tot;e[tot]=u;dep[tot++]=deep;
    for(int i=head[u];i!=-1;i=a[i].nex)
    {
        int v=a[i].to;
        if(pos[v]==-1)
        {
            dis[v]=dis[u]+a[i].w;
            fa[v]=u;
            dfs(v,deep+1);
            e[tot]=u;dep[tot++]=deep;
        }
    }
}
void rmq(int n)//2
{
    for(int i=1;i<=n;i++)f[i][0]=i;
    for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i+(1<<j)-1<=n;i++)
    {
        if(dep[f[i][j-1]]<dep[f[i+(1<<(j-1))][j-1]]) f[i][j]=f[i][j-1];
        else f[i][j]=f[i+(1<<(j-1))][j-1];
    }
}
int RMQ(int l,int r)
{
    int k=(int)(log((double)(r-l+1))/log(2.0));
    if(dep[f[l][k]]<dep[f[r-(1<<k)+1][k]]) return f[l][k];
    else return f[r-(1<<k)+1][k];
}
int lca(int x,int y)//3
{
    if(pos[x]<pos[y]) return e[RMQ(pos[x],pos[y])];
    else return e[RMQ(pos[y],pos[x])];
}
ll cal(int x,int y)
{
    return dis[x]+dis[y]-2*dis[lca(x,y)];
}
int main()
{
    int T,cas=1;
    //scanf("%d",&T);
    //while(T--)
    {
        init();
        scanf("%d %d",&n,&q);
        for(int i=0;i<n-1;i++)
        {
            int x,y;ll z=1;
            scanf("%d%d",&x,&y);
            add(x,y,z);
            add(y,x,z);
        }
        dfs(1,0);
        rmq(2*n-1);//4
        ll ans=0;
        while(q--)
        {
            int m,ls=1,u=-1,v=-1,x,fg=1;
            scanf("%d",&m);
            rep(j,1,m){
                scanf("%d",&x);
                if(x==1||fa[x]==1) continue;
                else if(u==-1) u=fa[x];
                else if(lca(x,u)==x) continue;
                else if(lca(x,u)==u) u=fa[x];
                else if(lca(fa[x],u)==fa[x]) continue;
                else fg=0;
            }
            if(fg) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值