11.5 T1.attack(拓扑+lca)

61 篇文章 0 订阅
7 篇文章 0 订阅

这里写图片描述

分析:
如果整个图是一棵树,那么针对每个询问
必经路径就是k个点的lca到根节点的路径
实际上扩展成一个普通的图也是这样

听男生们在讨论的时候,说这道题和灾难那道题”一样“
我仔细一想,确实哎,
我们现在手上的图是一个DAG
首先我们把ta的拓扑序计算出来
之后构造一个“拓扑树”
假设当前点是x,x的祖先一定都在拓扑树中了

我们要找到与原图中直接指向x的所有点在拓扑树中的lca,把x挂在lca下

(根节点到x的路径一定会经过lca)
处理询问的时候,我们只要在拓扑树上寻找这k个点的lca,输出lca到根节点的结点个数即可

//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>

using namespace std;

const int INF=0x33333333;
const int N=50005;
struct node{
    int x,y,nxt;
};
node way[N<<1],e[N<<1];
int st[N],tot=0,n,m,q,ste[N],tet=0;
int deep[N],pre[N][20],lg,f[N],cnt[N],Q[N],in[N],tou,wei;
bool vis[N];

void add(int u,int w)                                            //用来top的 
{
    tet++;
    e[tet].x=u;e[tet].y=w;e[tet].nxt=ste[u];ste[u]=tet;
}

void ad(int u,int w)                                            //用来建树的 
{ 
    tot++;
    way[tot].x=u;way[tot].y=w;way[tot].nxt=st[u];st[u]=tot;
}

int LCA(int u,int w)
{
    if (deep[u]<deep[w]) swap(u,w);
    int d=deep[u]-deep[w];
    if (d)
        for (int i=0;i<=lg&&d;i++,d>>=1)
            if (d&1)
               u=pre[u][i];
    if (u==w) return u;
    for (int i=lg;i>=0;i--)
        if (pre[u][i]!=pre[w][i])
        {
            u=pre[u][i];
            w=pre[w][i];
        }
    return pre[u][0];
}

void Top()
{
    tou=wei=0;
    for (int i=1;i<=n;i++) if (in[i]==0) Q[++wei]=i,in[i]=INF;;
    while (tou<wei)
    {
        int now=Q[++tou],lca;
        for (int i=ste[now];i;i=e[i].nxt)
        {
            in[e[i].y]--;
            if (in[e[i].y]==0) {
                Q[++wei]=e[i].y;
                in[e[i].y]=INF;
            }
        }   
    }
}

void doit()
{
    memset(pre,0,sizeof(pre));
    deep[1]=1;
    for (int j=2;j<=wei;j++)
    {
        int now=Q[j];
        int lca=-1;
        for (int i=st[now];i;i=way[i].nxt)                   //原图中前驱的lca 
            if (lca==-1) lca=way[i].y;
            else lca=LCA(lca,way[i].y);
        deep[now]=deep[lca]+1;
        pre[now][0]=lca;
        for (int i=1;i<=lg;i++)                              //一边添加,一边维护pre 
            pre[now][i]=pre[pre[now][i-1]][i-1];
    }
}

int main()
{
    //freopen("attack.in","r",stdin);  
    //freopen("attack.out","w",stdout);
    memset(in,0,sizeof(in));
    scanf("%d%d%d",&n,&m,&q);
    lg=log(n)/log(2)+1;

    for (int i=1;i<=m;i++)
    {
        int u,w;
        scanf("%d%d",&u,&w);
        add(u,w); ad(w,u);
        in[w]++;
    }

    Top();
    doit();
    for (int i=1;i<=q;i++)
    {
        int x,u,now;
        scanf("%d",&x);
        scanf("%d",&now);
        for (int j=2;j<=x;j++)
        {
            scanf("%d",&u);
            now=LCA(u,now);
        }
        printf("%d\n",deep[now]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值