1. 程式人生 > >洛谷 P2542 [AHOI2005]航線規劃 解題報告

洛谷 P2542 [AHOI2005]航線規劃 解題報告

tin HR 當前 星際 能夠 min CA 所在 str

P2542 [AHOI2005]航線規劃

題目描述

對Samuel星球的探險已經取得了非常巨大的成就,於是科學家們將目光投向了Samuel星球所在的星系——一個巨大的由千百萬星球構成的Samuel星系。

星際空間站的Samuel II巨型計算機經過長期探測,已經鎖定了Samuel星系中許多星球的空間坐標,並對這些星球從1開始編號1、2、3……。

一些先遣飛船已經出發,在星球之間開辟探險航線。

探險航線是雙向的,例如從1號星球到3號星球開辟探險航線,那麽從3號星球到1號星球也可以使用這條航線。

例如下圖所示:

技術分享圖片

在5個星球之間,有5條探險航線。

A、B兩星球之間,如果某條航線不存在,就無法從A星球抵達B星球,我們則稱這條航線為關鍵航線。

顯然上圖中,1號與5號星球之間的關鍵航線有1條:即為4-5航線。

然而,在宇宙中一些未知的磁暴和行星的沖撞,使得已有的某些航線被破壞,隨著越來越多的航線被破壞,探險飛船又不能及時回復這些航線,可見兩個星球之間的關鍵航線會越來越多。

假設在上圖中,航線4-2(從4號星球到2號星球)被破壞。此時,1號與5號星球之間的關鍵航線就有3條:1-3,3-4,4-5。

小聯的任務是,不斷關註航線被破壞的情況,並隨時給出兩個星球之間的關鍵航線數目。現在請你幫助完成。

輸入輸出格式

輸入格式:

第一行有兩個整數N,M。表示有N個星球(1< N < 30000),初始時已經有M條航線(1 < M < 100000)。隨後有M行,每行有兩個不相同的整數A、B表示在星球A與B之間存在一條航線。接下來每行有三個整數C、A、B。C為1表示詢問當前星球A和星球B之間有多少條關鍵航線;C為0表示在星球A和星球B之間的航線被破壞,當後面再遇到C為1的情況時,表示詢問航線被破壞後,關鍵路徑的情況,且航線破壞後不可恢復; C為-1表示輸入文件結束,這時該行沒有A,B的值。被破壞的航線數目與詢問的次數總和不超過40000。

輸出格式:

對每個C為1的詢問,輸出一行一個整數表示關鍵航線數目。

說明

我們保證無論航線如何被破壞,任意時刻任意兩個星球都能夠相互到達。在整個數據中,任意兩個星球之間最多只可能存在一條直接的航線。


思路:
有一個常見套路:對於刪除一些邊,采用離線逆序處理。

  1. 建要刪去的邊刪去
  2. 對刪去後的邊進行縮點,得到一顆樹
  3. 問題轉化為求兩點之間邊數
  4. 逆序連邊將處於環上的邊權置0,采用樹鏈剖分

細節:因為是點權下放至邊權,所以每次不對LCA進行操作

PS:無向圖建新圖的一個錯誤我拍了2個小時mmp(居然還有60分)
錯誤的:

void New()
{
    for(int i=1;i<=n0;i++)
        for(int j=head0[i];j;j=next0[j])
            if(edge0[j]&&ha[i]!=ha[to0[j]])
                add(ha[i],ha[to0[j]]),add(ha[to0[j]],ha[i]);
}

正確的:

void New()
{
    for(int i=1;i<=n0;i++)
        for(int j=head0[i];j;j=next0[j])
            if(edge0[j]&&ha[i]!=ha[to0[j]])
                add(ha[i],ha[to0[j]]);
}

Code:

#include <cstdio>
#include <cstring>
#include <map>
#define mid (l+r>>1)
#define Mid (L[id]+R[id]>>1)
#define ls id<<1
#define rs id<<1|1
using namespace std;
const int N=30010;
const int M=100010;
int head0[N],cnt0=0,to0[M<<1],next0[M<<1],edge0[M<<1];
void add0(int u,int v,int w)
{
    next0[++cnt0]=head0[u];edge0[cnt0]=w;to0[cnt0]=v;head0[u]=cnt0;
}
int head[N],cnt=0,to[M<<1],next[M<<1];
void add(int u,int v)
{
    next[++cnt]=head[u];to[cnt]=v;head[u]=cnt;
}
map <int,map <int,int > > del;//要刪去的邊
int aska[N],askb[N],acnt=0,n0,m,n,ans[N],wcnt,typ[N];
void Delete()
{
    for(int i=1;i<=n0;i++)
        for(int j=head0[i];j;j=next0[j])
            if(del[i][to0[j]])
                edge0[j]=0;
}
int s[N],tot=0,time=0,low[N],dfn[N],in[N],ha[N];
void tarjan(int now,int fa)
{
    dfn[now]=low[now]=++time;
    s[++tot]=now,in[now]=1;
    for(int i=head0[now];i;i=next0[i])
    {
        if(!edge0[i]) continue;
        int v=to0[i];
        if(!dfn[v])
        {
            tarjan(v,now);
            low[now]=min(low[now],low[v]);
        }
        else if(in[v]&&v!=fa)
            low[now]=min(low[now],dfn[v]);
    }
    if(dfn[now]==low[now])
    {
        int tmp;
        n++;
        do
        {
            tmp=s[tot--];
            in[tmp]=0;
            ha[tmp]=n;
        }while(tmp!=now);
    }
}
void New()
{
    for(int i=1;i<=n0;i++)
        for(int j=head0[i];j;j=next0[j])
            if(edge0[j]&&ha[i]!=ha[to0[j]])
                add(ha[i],ha[to0[j]]);
}
int top[N],f[N],ws[N],siz[N],dep[N];
void dfs1(int now)
{
    siz[now]++;
    for(int i=head[now];i;i=next[i])
    {
        int v=to[i];
        if(f[now]!=v)
        {
            f[v]=now;
            dep[v]=dep[now]+1;
            dfs1(v);
            siz[now]+=siz[v];
            if(siz[ws[now]]<siz[v])
                ws[now]=v;
        }
    }
}
void dfs2(int now,int anc)
{
    dfn[now]=++time;
    top[now]=anc;
    if(!ws[now]) return;
    dfs2(ws[now],anc);
    for(int i=head[now];i;i=next[i])
        if(!dfn[to[i]])
            dfs2(to[i],to[i]);
}
int dat[N<<2],L[N<<2],R[N<<2];
void build(int id,int l,int r)
{
    dat[id]=(r+1-l);
    L[id]=l,R[id]=r;
    if(l==r) return;
    build(ls,l,mid);
    build(rs,mid+1,r);
}
void change(int id,int l,int r)
{
    if(!dat[id]) return;
    if(L[id]==l&&R[id]==r)
    {
        dat[id]=0;
        return;
    }
    if(r<=Mid) change(ls,l,r);
    else if(l>Mid) change(rs,l,r);
    else change(ls,l,Mid),change(rs,Mid+1,r);
    dat[id]=dat[ls]+dat[rs];
}
int query(int id,int l,int r)
{
    if(!dat[id]) return 0;
    if(L[id]==l&&R[id]==r)
        return dat[id];
    if(r<=Mid) return query(ls,l,r);
    else if(l>Mid) return query(rs,l,r);
    else return query(ls,l,Mid)+query(rs,Mid+1,r);
}
void t_change(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
        {
            change(1,dfn[top[x]],dfn[x]);
            x=f[top[x]];
        }
        else
        {
            change(1,dfn[top[y]],dfn[y]);
            y=f[top[y]];
        }
    }
    if(dfn[x]!=dfn[y])
        change(1,min(dfn[x],dfn[y])+1,max(dfn[x],dfn[y]));
}
int t_query(int x,int y)
{
    int ans=0;
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])
        {
            ans+=query(1,dfn[top[x]],dfn[x]);
            x=f[top[x]];
        }
        else
        {
            ans+=query(1,dfn[top[y]],dfn[y]);
            y=f[top[y]];
        }
    }
    if(dfn[x]!=dfn[y])
        ans+=query(1,min(dfn[x],dfn[y])+1,max(dfn[x],dfn[y]));
    return ans;
}
int cntt[N];
int main()
{
    scanf("%d%d",&n0,&m);
    int u,v;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        add0(u,v,1),add0(v,u,1);
    }
    int a,b,c;
    scanf("%d%d%d",&c,&a,&b);
    while(2333)
    {
        aska[++acnt]=a,askb[acnt]=b,typ[acnt]=c;
        if(!c)
            del[a][b]=1,del[b][a]=1;
        scanf("%d",&c);
        if(c==-1)
            break;
        scanf("%d%d",&a,&b);
    }
    Delete();//刪掉訪問的邊
    tarjan(1,0);//縮點
    New();//建立新圖
    memset(dfn,0,sizeof(dfn));
    time=0;
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    for(int i=acnt;i;i--)
    {
        if(typ[i]) ans[++wcnt]=t_query(ha[aska[i]],ha[askb[i]]);
        else t_change(ha[aska[i]],ha[askb[i]]);
    }
    for(int i=wcnt;i;i--)
        printf("%d\n",ans[i]);
    return 0;
}

2018.6.24

洛谷 P2542 [AHOI2005]航線規劃 解題報告