1. 程式人生 > >Query on a tree IV(SPOJ - QTREE4)

Query on a tree IV(SPOJ - QTREE4)

app 註意 val maximum initial lin main direct each

題目描述:

You are given a tree (an acyclic undirected connected graph) with N nodes, and nodes numbered 1,2,3...,N. Each edge has an integer value assigned to it(note that the value can be negative). Each node has a color, white or black. We define dist(a, b) as the sum of the value of the edges on the path from node a to node b.

All the nodes are white initially.

We will ask you to perfrom some instructions of the following form:

  • C a : change the color of node a.(from black to white or from white to black)
  • A : ask for the maximum dist(a, b), both of node a and node b must be white(a can be equal to b). Obviously, as long as there is a white node, the result will alway be non negative.

給出一棵n個結點的樹,每條邊有邊權。一開始,每個點都是白色的。
再給出q個詢問,每次詢問有兩種操作:

1.C a 表示改變a結點的顏色,黑變白,白變黑。
2.A 表示詢問樹上某兩個白點(可以是同一個點)間最遠距離
註意原題卡常,可以嘗試換用clang編譯器提交。

思路:

QTree 5 的進化版。。(為甚是這個順序我也不清楚。。)

主要操作和QTree4的操作差不多,詳細內容可見那題題解:Query on a tree V

上一題的思想是在每個點開一個可刪堆\(Q1\),然後利用點分樹優化查詢與更新,這次其實也差不多只不過它並沒有給你確定其中一個點,那麽接下來的操作就比較有趣了

為了避免多次枚舉,我們在每個點上多開一個隊列\(Q2\)

,存的內容是距離每個兒子上最遠的點到自身的距離,聽起來有些繞,好好理解一下。。然後每次更新時,只要註意當前的兒子的最遠距離(記錄在原先的那個隊列中)——也就是\(Q1\)\(Top\)被更新掉時,就要對應的去更新它父親的\(Q2\),註意要算上兒子到父親的距離,為了方便,我的\(Q1\)中存的值就是算上它到父親的距離的。那麽若要找經過該點的最大距離時,就只用在它所對應的\(Q2\)中找出最大的兩個值的和即可(註意如果只有一個值的時候,要返回0,因為題意說明可以是同一個點也算)

所以代碼中最關鍵的兩點就是對於這三種隊列的更新了!仔細理解。

最後就是把每個點的最大值存在最後一個隊列\(A\)中,同理每次一個點的\(Q2\)中的最大兩值和改變時,都要在\(A\)中進行對應的更新(刪除舊值,加入新值),那麽最終查詢答案只要返回\(A\)\(Top\)即可,至於沒有白點的判斷,只要維護一個\(tot\),計數白點數量,不必多說!

還有一個需要說的就是點分樹的存法,由於這題時限,不能用常規\(log\)\(LCA\)來算距離,可以全部預處理出來存一個正向表(或\(vector\))中。存每個兒子的每個父親以及距離,這些均可以在一個\(dfs\)中求出

代碼

#include<cstdio>
#include<cstring>
#include<queue>
#define FOR(i,l,r) for(register int i=l,END=r;i<=END;i++)
#define DOR(i,r,l) for(register int i=r,END=l;i>=END;i--)
#define loop(i,n) for(register int i=0,END=n;i<END;i++)
#define mms(a,x) memset(a,x,sizeof a)
#define sf scanf
#define pf printf
using namespace std;
const int N=1e5+5,INF=1e9;
int n;
struct Graph{
    int tot,to[N*20],nxt[N*20],len[N*20],head[N];
    void add(int x,int y,int z){tot++;to[tot]=y;nxt[tot]=head[x];len[tot]=z;head[x]=tot;}
    void clear(){mms(head,-1);tot=0;}
    #define EOR(A,i,x) for(register int i=A.head[x];i!=-1;i=A.nxt[i])
}G,G2;//原樹和點分樹的路徑 

struct Heap{//可刪堆 
    priority_queue<int>Q,del;
    void Push(int x){if(x!=-INF)Q.push(x);}
    void Del(int x){if(x!=-INF)del.push(x);}
    void upd(){while(!del.empty()&&Q.top()==del.top())Q.pop(),del.pop();}
    int Size(){return Q.size()-del.size();}
    int Top(){upd();if(Q.empty())return -INF;return Q.top();}
    int Sum2(){
        upd();int siz=Size();
        if(!siz)return -INF;
        if(siz==1)return 0;
        int mx1=Top();Q.pop();
        int mx2=Top();Q.push(mx1);
        return max(mx1+mx2,0);
    }
}A,B[N],C[N];//C便是題解中的Q1,B便是題解中的Q2

bool col[N];//記錄顏色 

struct DAC_Tree{//點分樹 
    bool vis[N];
    int sz[N],mx[N],fa[N];
    int t_sz,center;
    void get_dis(int x,int f,int dis,int center){//預處理上面的G2 
        G2.add(x,center,dis);
        EOR(G,i,x){
            int v=G.to[i];
            if(v==f||vis[v])continue;
            get_dis(v,x,dis+G.len[i],center);
        }
    }
    void get_center(int x,int f){//尋找重心 
        sz[x]=1,mx[x]=0;
        EOR(G,i,x){
            int v=G.to[i];
            if(v==f||vis[v])continue;
            get_center(v,x);
            sz[x]+=sz[v];
            mx[x]=max(mx[x],sz[v]);
        }
        mx[x]=max(mx[x],t_sz-sz[x]);
        if(!center||mx[center]>mx[x])center=x;
    }
    void DAC(int x){//點分治 
        vis[x]=1;
        G2.add(x,x,0);
        EOR(G,i,x){
            int v=G.to[i];
            if(vis[v])continue;
            get_dis(v,x,G.len[i],x);
            center=0,t_sz=sz[v];
            get_center(v,x);
            fa[center]=x;
            DAC(center);
        }
    }
    
    //以下兩個函數為本題關鍵,註意三個隊列的更新 
    void insert(int x){//把一個點變白 
        int sum=B[x].Sum2();
        B[x].Push(0);
        int nsum=B[x].Sum2();
        if(nsum!=sum){
            A.Del(sum);
            A.Push(nsum);
        }
        EOR(G2,i,x){
            if(G2.nxt[i]==-1)break;
            int v=G2.to[i],f=G2.to[G2.nxt[i]],dis=G2.len[G2.nxt[i]],tp=C[v].Top();
            C[v].Push(dis);
            int ntp=C[v].Top();
            if(ntp!=tp){
                sum=B[f].Sum2();
                B[f].Del(tp);
                B[f].Push(dis);
                nsum=B[f].Sum2();
                if(nsum!=sum){
                    A.Del(sum);
                    A.Push(nsum);
                }
            }
        }
    }
    void erase(int x){//把一個點變黑 
        int sum=B[x].Sum2();
        B[x].Del(0);
        int nsum=B[x].Sum2();
        if(nsum!=sum){
            A.Del(sum);
            A.Push(nsum);
        }
        EOR(G2,i,x){
            if(G2.nxt[i]==-1)break;
            int v=G2.to[i],dis=G2.len[G2.nxt[i]],f=G2.to[G2.nxt[i]],tp=C[v].Top();
            C[v].Del(dis);
            int ntp=C[v].Top();
            if(ntp!=tp){
                sum=B[f].Sum2();
                if(tp!=-INF)B[f].Del(tp);
                B[f].Push(C[v].Top());
                nsum=B[f].Sum2();
                if(nsum!=sum){
                    A.Del(sum);
                    A.Push(nsum);
                }
            }           
        }
    }
    //預處理 
    void init(){
        center=0,t_sz=n;
        get_center(1,0);
        DAC(center);
        FOR(i,1,n)insert(i);
    }
}Tr;

int tot;
int main(){
    G.clear(),G2.clear();
    sf("%d",&n);
    loop(i,n-1){
        int x,y,z;
        sf("%d%d%d",&x,&y,&z);
        G.add(x,y,z),G.add(y,x,z);
    }
    Tr.init();
    int q;tot=n;
    sf("%d",&q);
    while(q--){
        char op[5];
        sf("%s",op);
        if(op[0]=='A'){
            if(!tot)pf("They have disappeared.\n");//無白點 
            else pf("%d\n",A.Top());
        }
        else { 
            int a;sf("%d",&a); 
            col[a]^=1;
            if(col[a]){Tr.erase(a);tot--;}
            else {Tr.insert(a);tot++;}
        }
    }
    return 0;
}

Query on a tree IV(SPOJ - QTREE4)