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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章