1. 程式人生 > >BZOJ 2959: 長跑 解題報告

BZOJ 2959: 長跑 解題報告

2959: 長跑

Description

  某校開展了同學們喜聞樂見的陽光長跑活動。為了能“為祖國健康工作五十年”,同學們紛紛離開寢室,離開教室,離開實驗室,到操場參加3000米長跑運動。一時間操場上熙熙攘攘,摩肩接踵,盛況空前。
  為了讓同學們更好地監督自己,學校推行了刷卡機制。
  學校中有n個地點,用1到n的整數表示,每個地點設有若干個刷卡機。
  有以下三類事件:
  1、修建了一條連線A地點和B地點的跑道。
  2、A點的刷卡機臺數變為了B。
  3、進行了一次長跑。問一個同學從A出發,最後到達B最多可以刷卡多少次。具體的要求如下:
  當同學到達一個地點時,他可以在這裡的每一臺刷卡機上都刷卡。但每臺刷卡機只能刷卡一次,即使多次到達同一地點也不能多次刷卡。
  為了安全起見,每條跑道都需要設定一個方向,這條跑道只能按照這個方向單向通行。最多的刷卡次數即為在任意設定跑道方向,按照任意路徑從A地點到B地點能刷卡的最多次數。

Input

  輸入的第一行包含兩個正整數n,m,表示地點的個數和操作的個數。
  第二行包含n個非負整數,其中第i個數為第個地點最開始刷卡機的臺數。
  接下來有m行,每行包含三個非負整數P,A,B,P為事件型別,A,B為事件的兩個引數。
  最初所有地點之間都沒有跑道。
  每行相鄰的兩個數之間均用一個空格隔開。表示地點編號的數均在1到n之間,每個地點的刷卡機臺數始終不超過10000,P=1,2,3。

Output

  輸出的行數等於第3類事件的個數,每行表示一個第3類事件。如果該情況下存在一種設定跑道方向的方案和路徑的方案,可以到達,則輸出最多可以刷卡的次數。如果A不能到達B,則輸出-1。


思路:用LCT維護鏈的資訊,用並查集進行縮點。

注意到每次縮點後只有虛邊連的邊是不對的,所以在access時重新練一下虛邊和詢問的時候詢問並查集的根就可以了,但是bzoj我還是沒卡過去...


Code:

#include <cstdio>
#include <cctype>
#define ll long long
#define fa par[now]
#define ls ch[now][0]
#define rs ch[now][1]
const int N=150010;
int read()
{
    int x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) {x=x*10+c-'0';c=getchar();}
    return x;
}
int ch[N][2],par[N],tag[N],s[N],F[N],tot,tmp,n,m;
ll sum[N],dat[N],Dat[N];
int Find(int x){return F[x]=F[x]==x?x:Find(F[x]);}
int identity(int now){return ch[fa][1]==now;}
bool isroot(int now){return ch[fa][0]==now||ch[fa][1]==now;}
void updata(int now){sum[now]=sum[ls]+sum[rs]+dat[now];}
void Reverse(int now){tag[now]^=1,tmp=ls,ls=rs,rs=tmp;}
void connect(int f,int now,int typ){ch[fa=f][typ]=now;}
void pushdown(int now)
{
    if(tag[now])
    {
        if(ls) Reverse(ls);
        if(rs) Reverse(rs);
        tag[now]^=1;
    }
}
void Rotate(int now)
{
    int p=Find(fa),typ=identity(now);
    connect(p,ch[now][typ^1],typ);
    if(isroot(p)) connect(par[p],now,identity(p));
    else fa=Find(par[p]);
    connect(now,p,typ^1);
    updata(p),updata(now);
}
void splay(int now)
{
    while(isroot(now)) s[++tot]=now,now=fa;s[++tot]=now;
    while(tot) pushdown(s[tot--]);now=s[1];
    for(;isroot(now);Rotate(now))
        if(isroot(fa))
            Rotate(identity(now)^identity(fa)?now:fa);
}
void access(int now)
{
    for(int las=0;now;las=now,now=(fa=Find(fa)))
        splay(now),rs=las,updata(now);
}
void evert(int now){access(now),splay(now),Reverse(now);}
int findroot(int now)
{
    access(now);
    splay(now);
    while(ls) now=ls;
    return now;
}
void dfs(int now,int anc)
{
    if(!now) return;
    F[now]=anc;
    dfs(ls,anc),dfs(rs,anc);
}
void link(int u,int v)
{
    evert(u);
    if(findroot(v)!=u) par[u]=v;
    else dat[v]=sum[v],dfs(v,v),ch[v][0]=ch[v][1]=0;
}
void query(int u,int v)
{
    evert(u);
    if(findroot(v)!=u) puts("-1");
    else printf("%lld\n",sum[v]);
}
int main()
{
    freopen("data.in","r",stdin);
    freopen("dew.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;i++) F[i]=i,Dat[i]=sum[i]=dat[i]=read();
    for(int op,x,y,i=1;i<=m;i++)
    {
        op=read(),x=read(),y=read();
        if(op==1)
            link(Find(x),Find(y));
        else if(op==2)
        {
            int rt=Find(x);
            splay(rt);
            dat[rt]+=y-Dat[x];
            Dat[x]=y;
            updata(rt);
        }
        else
            query(Find(x),Find(y));
    }
    return 0;
}

2018.12.11