Alliances

树国是一个有n个城市的国家,城市编号为1∼n。连接这些城市的道路网络形如一棵树,
即任意两个城市之间有恰好一条路径。城市中有k个帮派,编号为1∼k。每个帮派会占据一些城市,以进行非法交易。有时帮派之间会结盟,这就使得城市更加不安全了。同一座城市中可能有多个帮派。
当一些帮派结成联盟时,他们会更加强大,同时也更加危险。他们所控制的城市数会显著增加。具体地,一个联盟控制的城市是联盟中所有帮派所占据的城市,再加上这些城市两两之间路径上的所有城市。
shy是树国的市长,他想要选择一个城市作为首都。在决定之前,他要先做一些调研。为此,他找来你帮他回答一些询问,你能做到吗?在每个询问中,shy会选择一个城市作为首都,同时会告诉你当前活跃的帮派的集合。在这个询问中,你只需要考虑给定的集合中的帮派,其他的帮派你可以当作不存在。已知给定集合中的这些帮派结成了联盟,shy希望抓获联盟中的人,以得到关于整个联盟的一些信息。为此,他要找到被联盟控制的所有城市中离首都最近的一座城市到首都的距离。有可能首都本身就被控制了,此时答案为0。请注意,询问之间相互独立,互不影响。
 

输入描述:

输入的第一行包含一个整数n,代表树国中的城市数。
接下来n−1行,每行包含两个整数u和v,代表城市u和v之间存在一条道路。
接下来一行包含一个整数k,代表树国中的帮派数。
接下来k行,每行描述一个帮派。第i行的第一个整数c[i]代表第i个帮派占据的城市数,接下来c[i]个整数,代表被第i个帮派占据的城市。
接下来一行包含一个整数Q,代表询问数。
接下来Q行,每行描述一个询问。每行的前两个整数V和t[i]代表本次询问中的首都与需要考虑的帮派集合的大小。接下来t[i]个整数代表本次询问中需要考虑的帮派。.

输出描述:

对于每个询问,输出一行,包含一个整数,代表询问的答案。
示例1

输入

7
1 2
1 3
2 4
2 5
3 6
3 7
2
2 6 7
1 4
3
5 1 2
1 1 1
5 2 1 2

输出

2
1
1

备注:

对于30%的数据,1≤n,k,Q≤1000, 1≤每个帮派占据城市数之和≤1000, 1≤每个询问中考虑的帮派数之和≤1000  对于60%的数据,1≤n,k,Q≤100000, 1≤每个帮派占据城市数之和≤100000, 1≤每个询问中考虑的帮派数之和≤100000 对于100%的数据,1≤n,k,Q≤500000, 1≤每个帮派占据城市数之和≤500000, 1≤每个询问中考虑的帮派数之和≤500000
分析:首先,联盟或者帮派是形成一棵树的;
   如果首都如果向上不能到达树,那么直接输出树根与首都距离;
   否则首都一定向上到达树里某个点;
   为了找出这个点,利用dfs序,求出首都附近两个点,分别求与首都lca,距离取最小;
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <climits>
#include <cstring>
#include <string>
#include <set>
#include <bitset>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <cassert>
#include <ctime>
#define rep(i,m,n) for(i=m;i<=(int)n;i++)
#define inf 0x3f3f3f3f
#define mod 998244353
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define pi acos(-1.0)
#define pii pair<int,int>
#define sys system("pause")
#define ls (rt<<1)
#define rs (rt<<1|1)
#define all(x) x.begin(),x.end()
const int maxn=5e5+10;
const int N=2e5+10;
using namespace std;
ll gcd(ll p,ll q){return q==0?p:gcd(q,p%q);}
ll qmul(ll p,ll q,ll mo){ll f=0;while(q){if(q&1)f=(f+p)%mo;p=(p+p)%mo;q>>=1;}return f;}
ll qpow(ll p,ll q,ll mo){ll f=1;while(q){if(q&1)f=f*p%mo;p=p*p%mo;q>>=1;}return f;}
int n,m,k,t,dfn[maxn],st[maxn],anc[maxn],dep[maxn],fa[20][maxn],tot,cnt,head[maxn];
vi p[maxn];
struct node
{
    int to,nxt;
}e[maxn<<1];
void add(int x,int y)
{
    e[cnt].to=y;
    e[cnt].nxt=head[x];
    head[x]=cnt++;
}
set<int>bl[maxn];
void dfs(int x,int y)
{
    int i;
    dfn[++tot]=x;
    st[x]=tot;
    dep[x]=dep[y]+1;
    for(i=1;fa[i-1][fa[i-1][x]];i++)
    {
        fa[i][x]=fa[i-1][fa[i-1][x]];
    }
    for(i=head[x];i!=-1;i=e[i].nxt)
    {
        int z=e[i].to;
        if(z==y)continue;
        fa[0][z]=x;
        dfs(z,x);
    }
}
int lca(int x,int y)
{
    int i;
    if(dep[x]<dep[y])swap(x,y);
    for(i=19;i>=0;i--)if(dep[fa[i][x]]>=dep[y])x=fa[i][x];
    if(x==y)return x;
    for(i=19;i>=0;i--)
    {
        if(fa[i][x]!=fa[i][y])
        {
            x=fa[i][x];
            y=fa[i][y];
        }
    }
    return fa[0][x];
}
int main(){
    int i,j;
    scanf("%d",&n);
    rep(i,1,n)head[i]=-1;
    rep(i,1,n-1)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    scanf("%d",&m);
    rep(i,1,m)
    {
        scanf("%d",&j);
        while(j--)scanf("%d",&k),p[k].pb(i);
    }
    dfs(1,0);
    rep(i,1,n)
    {
        rep(j,0,p[i].size()-1)
        {
            if(anc[p[i][j]]==0)anc[p[i][j]]=i;
            else anc[p[i][j]]=lca(anc[p[i][j]],i);
            bl[p[i][j]].insert(st[i]);
        }
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int cap,fa=-1,pr=-1,nt=-1;
        scanf("%d",&cap);
        scanf("%d",&j);
        while(j--)
        {
            scanf("%d",&k);
            auto x=bl[k].lower_bound(st[cap]);
            if(x!=bl[k].end())
            {
                if(nt==-1)nt=*x;
                else nt=min(nt,*x);
            }
            if(x!=bl[k].begin()&&(x==bl[k].end()||*x>st[cap]))--x;
            if(x!=bl[k].end()&&*x<=st[cap])
            {
                if(pr==-1)pr=*x;
                else pr=max(pr,*x);
            }
            if(fa==-1)fa=anc[k];
            else fa=lca(anc[k],fa);
        }
        int x_fa=lca(fa,cap);
        if(x_fa==cap||x_fa!=fa)printf("%d\n",dep[fa]+dep[cap]-2*dep[x_fa]);
        else
        {
            int ret1=1e9,ret2=1e9;
            if(pr!=-1)pr=dfn[pr],ret1=dep[cap]-dep[lca(pr,cap)];
            if(nt!=-1)nt=dfn[nt],ret2=dep[cap]-dep[lca(nt,cap)];
            printf("%d\n",min(ret1,ret2));
        }
    }
    return 0;
}

转载于:https://www.cnblogs.com/dyzll/p/7527644.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值