codeforces 757F Team Rocket Rises Again(最短路+支配树)

今天刚刚看了支配树,然而恕我太弱并没有看明白Lengauer-Tarjan算法,比较懵QAQ……不过我倒是学会了在DAG上构建支配树,就贴一道简单题过来吧O(∩_∩)O。

如何在DAG上求支配树?

有向无环图(DAG)里我们可以按照拓扑序构建支配树。

假设当前我们构造到拓扑序中第x个节点编号为v,那么此时支配树中已经有拓扑序第1~x-1个节点了。考虑所有能够直接到达v的节点,对于这些节点我们求出它们在支配树上的最近公共祖先Lca(所以要反向建一份图g2),这个点Lca就是点v在支配树上的父亲。

这样,支配树就建好了

用倍增求LCA的话,时间复杂度是O((n+m)log2n)。

题目链接:codeforces 757 F
题解:这个题的话,跑出最短路图,再在最短路图上建支配树,求最大的子树大小。
code(有参考网上神犇们的代码)

#include<iostream>
#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 1LL<<60
using namespace std;

inline int read()
{
    char c=getchar(); int num=0,f=1;
    while (c<'0'||c>'9') { if (c=='-') f=-1; c=getchar(); }
    while (c<='9'&&c>='0') { num=num*10+c-'0'; c=getchar(); }
    return num*f;
}

struct edge{
    int to,ne;
    long long val;
}e[600005],g1[600005],g2[600005];
//e[]是原图,g1[]是最短路图,g2[]和g1[]边的方向相反
int head[200005],head1[200005],head2[200005];
int que[4000005];
int d[200005],deep[200005],siz[200005];
int fa[200005][22];
int S,n,m,tot,tot1,tot2,ans;
bool vis[200005];
long long dis[200005];
void push(int x,int y,long long val)
{
    e[++tot].to=y; e[tot].val=val; e[tot].ne=head[x]; head[x]=tot;
}
void push1(int x,int y)
{
    g1[++tot1].to=y; g1[tot1].ne=head1[x]; head1[x]=tot1;
}
void push2(int x,int y)
{
    g2[++tot2].to=y; g2[tot2].ne=head2[x]; head2[x]=tot2;
}

void spfa(int S)
{
    for (int i=1;i<=n;i++) dis[i]=inf;
    int h=0,t=1; 
    dis[S]=0,vis[S]=true,que[t]=S;
    while (h<t)
    {
        int now=que[++h];
        for (int i=head[now];i;i=e[i].ne)
        {
            int v=e[i].to;
            if (dis[v]>dis[now]+e[i].val)
            {
                dis[v]=dis[now]+e[i].val;
                if (!vis[v]) vis[v]=true,que[++t]=v;
            }
        }
        vis[now]=false;
    }
}
void dfs1(int now)   //找出最短路图中的边
{
    vis[now]=true;
    for (int i=head[now];i;i=e[i].ne)
    {
        int v=e[i].to;
        if (dis[v]==dis[now]+e[i].val)
        {
            push1(now,v);
            push2(v,now);
            d[v]++;
            if (!vis[v]) dfs1(v);
        }
    }
}
void dfs2(int now)     //求出支配树各节点的size,更新答案
{
    siz[now]=1;
    for (int i=head[now];i;i=e[i].ne)
    {
        int v=e[i].to;
        dfs2(v);
        siz[now]+=siz[v];
    }
    if (now!=S) ans=max(ans,siz[now]);
}
int lca(int x,int y)   //倍增lca
{
    if (x==y) return x;
    if (deep[x]<deep[y]) swap(x,y);
    for (int i=20;i>=0;i--)
     if (deep[fa[x][i]]>=deep[y]) x=fa[x][i];
    if (x==y) return x;
    for (int i=20;i>=0;i--)
     if (fa[x][i]!=fa[y][i]) 
      x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
void topsort()     //拓扑排序
{
    int h=0,t=1;
    que[t]=S,deep[S]=1;
    while (h<t)
    {
        int now=que[++h];
        for (int i=head1[now];i;i=g1[i].ne)
        {
            int v=g1[i].to;
            d[v]--;
            if (!d[v]) 
            {
                que[++t]=v;
                int Lca=0;
                for (int j=head2[v];j;j=g2[j].ne)
                {
                    int vv=g2[j].to;
                    if (!Lca) Lca=vv;
                     else Lca=lca(vv,Lca);
                }   //找到v的支配点Lca,把v点加入支配树里
                push(Lca,v,0);
                fa[v][0]=Lca,deep[v]=deep[Lca]+1;
                for (int j=1;j<=20;j++) fa[v][j]=fa[fa[v][j-1]][j-1];
            }
        }
    }
}
int main()
{
    n=read(); m=read(); S=read();
    for (int i=1;i<=m;i++)
    {
        int x=read(),y=read(),z=read();
        push(x,y,(long long)z);
        push(y,x,(long long)z);
    }
    spfa(S); dfs1(S);    //求最短路图
    memset(head,0,sizeof(head)),tot=0;  
    topsort(); dfs2(S);    //建支配树
    printf("%d\n",ans);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章