點分治&&動態點分治學習筆記

忽然發現網上關於點分和動態點分的教程好像不多……蒟蒻開篇blog記錄一下吧……由於這是個大傻逼,可能有不少地方寫錯,歡迎在下面提出php

參考文獻:http://www.javashuo.com/article/p-bbmzcafp-g.htmlhtml

    https://blog.csdn.net/qq_39553725/article/details/77542223node

    https://blog.csdn.net/zzkksunboy/article/details/70244945ios

 

前言c++

  通常來講,對於大規模處理樹上路徑,咱們會對整棵樹進行分治。而樹分治有兩種,一種是邊分治,不在本文考慮範圍內(主要是我不會)因此暫且不提,另一種就是點分治。git

  澱粉質,啊呸,點分治,顧名思義,就是把樹上的節點拆開來進行分治,每一次把樹給拆成好幾棵子樹,而後再繼續遞歸下去,直到算出全部的答案數組

分治點數據結構

  既然是分治,咱們確定每一次都要選擇一個點,從他開始分治下去。那麼考慮咱們如何選擇這個點呢?咱們發現,每一次處理完一個點以後,咱們都要遞歸進它的子樹,那麼時間複雜度受到它最大的子樹的大小的影響。好比,若是原樹是一條鏈,咱們選擇鏈首,一路遞歸下去,時間複雜度毫無疑問是$O(n^2)$的(那還不如別學了直接打暴力)。因此,咱們要讓每一次選到的點的最大子樹最小。ide

  實際上,一棵樹的最大子樹最小的點有一個名稱,叫作重心。post

時間複雜度

  考慮一下爲何每一次都選擇重心,時間複雜度就是對的呢?

  由於重心有一個很重要的性質,每個子樹的大小都不超過$n/2$

  考慮爲何呢?咱們能夠用反證法來證實(這裏感謝zzk大佬的證實)

 

  考慮有如上這麼一棵樹,其中點$u$是重心,$son[u]$表示$u$點的最大的子樹的大小,$v$是點$u$的最大子樹,且$size[v]>size[u]/2$

  由於$size[v]>size[u]/2$,其餘子樹加上點$u$的節點數小於$size[u]/2$,那麼不難發現,咱們選擇點$v$做爲重心,$son[v]=size[v]-1<son[u]$,那麼很明顯$u$不知足重心的定義

  因而每一次找到重心,遞歸的子樹大小是不超過原樹大小的一半的,那麼遞歸層數不會超過$O(logn)$層,時間複雜度爲$O(nlogn)$

求重心

  然而重心如何求呢?直接暴力,咱們$dfs$整棵樹,能夠$O(n)$的求出樹的重心

  仍是貼代碼好了

void findrt(int u,int fa){
    //sz表示子樹的大小,son表示點的最大子樹的大小
    //cmax(a,b)表示若是b>a則a=b
    //我的習慣這樣寫,或者直接寫成a=max(a,b) 
    sz[u]=1,son[u]=0;
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(vis[v]||v==fa) continue;
        findrt(v,u);
        sz[u]+=sz[v];
        cmax(son[u],sz[v]);
    }
    //size表示整棵樹的大小 
    //由於這是一棵無根樹,因此包括它的父親在內的那一坨也應該算做它的子樹 
    cmax(son[u],size-sz[u]);
    if(son[u]<mx) mx=son[u],rt=u;
}

 

  因而就能夠愉快的$O(n)$求出重心(~ ̄▽ ̄)~

實現

  仍是先貼代碼好了

void divide(int u){
    ans+=solve(u,0);//把當前節點的答案加上去 
    vis[u]=1;//把節點標記,防止陷入死循環 
    for(int i=head[u];i;i=Next[i]){
        //分別處理每一棵子樹 
        int v=ver[i];
        if(vis[v]) continue;
        ans-=solve(v,edge[i]);//容斥原理,下面說 
        mx=inf,rt=0,size=sz[v];
        //把全部信息更新,遞歸進子樹找重心,並繼續分治 
        findrt(v,0);
        divide(rt);
    }
}

上面其餘的應該都好理解,除了這一句 ans-=solve(v,edge[i]); 

考慮一下這棵樹

考慮一下,從點$1$出發的路徑有如下幾條

$1->4$

$1->4->6$

$1->2$

$1->2->3$

$1->2->5$

而後咱們爲了求貢獻,會將路徑兩兩合併

然而合併$1->2->3$和$1->2->5$這兩條路徑其實是不合法的,由於出現了重邊

因此要減去$2$這一棵子樹中的全部路徑兩兩合併的貢獻

而後回頭來看代碼 ans+=solve(u,0);  ans-=solve(v,edge[i]); 

看到沒?第二個參數不同,這樣在考慮子樹中兩兩合併時的貢獻時就不會把這一條邊的貢獻給漏掉了

而後只要遞歸繼續找就能夠(*^▽^*)

注意點

  實際上呢,點分的時候找重心的寫法有兩種,一種是上面的那個,另外一種我貼上來,就是在遞歸的時候寫的不同

 1 void divide(int u){
 2     ans+=solve(u,0);
 3     vis[u]=1;
 4     int totsz=size;
 5     for(int i=head[u];i;i=Next[i]){
 6         int v=ver[i];
 7         if(vis[v]) continue;
 8         ans-=solve(v,edge[i]);
 9         mx=inf,rt=0;
10         size=sz[v]>sz[u]?totsz-sz[u]:sz[v];//這裏應該這樣寫纔是對的 
11         findrt(v,0);
12         divide(rt);
13     }
14 }

  實際上這一種方法纔是對的,我上面寫的反而是錯的,然而時間複雜度並不會退化,具體的證實請看->這裏

例題

  講了這麼多……然而實際上彷佛並無什麼用……仍是來說幾道題好了……

//minamoto
#include<bits/stdc++.h>
#define N 40005
#define M 80005
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<15,stdin),p1==p2)?EOF:*p1++)
char buf[1<<15],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
inline int read(){
    #define num ch-'0'
    char ch;bool flag=0;int res;
    while(!isdigit(ch=getc()))
    (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getc());res=res*10+num);
    (flag)&&(res=-res);
    #undef num
    return res;
}
int ver[M],Next[M],head[N],edge[M];
int n,tot,root;ll k;
void add(int u,int v,int e){
    ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
    ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e;
}
int sz[N],vis[N],mx,size;
ll d[N],q[N],l,r;
void getroot(int u,int fa){
    sz[u]=1;int num=0;
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(v==fa||vis[v]) continue;
        getroot(v,u);
        sz[u]+=sz[v];
        cmax(num,sz[v]);
    }
    cmax(num,size-sz[u]);
    if(num<mx) mx=num,root=u;
}
void getdis(int u,int fa){
    q[++r]=d[u];
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(v==fa||vis[v]) continue;
        d[v]=d[u]+edge[i];
        getdis(v,u);
    }
}
ll calc(int u,int val){
    r=0;
    d[u]=val;
    getdis(u,0);
    ll sum=0;l=1;
    sort(q+1,q+r+1);
    while(l<r){
        if(q[l]+q[r]<=k) sum+=r-l,++l;
        else --r;
    }
    return sum;
}
ll ans=0;
void dfs(int u){
    ans+=calc(u,0);
    vis[u]=1;
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(vis[v]) continue;
        ans-=calc(v,edge[i]);
        size=sz[v];
        mx=inf;
        getroot(v,0);
        dfs(root);
    }
}
int main(){
    //freopen("testdata.in","r",stdin);
    n=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read(),e=read();
        add(u,v,e);
    }
    k=read();
    size=n;
    mx=inf;
    getroot(1,0);
    dfs(root);
    printf("%lld",ans);
    return 0;
}
tree

poj1741tree 
給一顆n個節點的樹,每條邊上有一個距離v。定義d(u,v)爲u到v的最小距離。給定k值,求有多少點對(u,v)使u到v的距離小於等於k。 

 

點分的板子……好像基本都是板子套進去……就是注意合併的時候二分保證複雜度

bzoj2152 聰聰可可

題目描述

聰聰和可但是兄弟倆,他們倆常常爲了一些雜事打起來,例如家中只剩下最後一根冰棍而兩人都想吃、兩我的都想玩兒電腦(但是他們家只有一臺電腦)……遇到這種問題,通常狀況下石頭剪刀布就行了,但是他們已經玩兒膩了這種低智商的遊戲。

他們的爸爸快被他們的爭吵煩死了,因此他發明了一個新遊戲:由爸爸在紙上畫n個「點」,並用n-1條「邊」把這n個「點」剛好連通(其實這就是一棵樹)。而且每條「邊」上都有一個數。接下來由聰聰和可可分別隨即選一個點(固然他們選點時是看不到這棵樹的),若是兩個點之間全部邊上數的和加起來剛好是3的倍數,則判聰聰贏,不然可可贏。

聰聰很是愛思考問題,在每次遊戲後都會仔細研究這棵樹,但願知道對於這張圖本身的獲勝機率是多少。現請你幫忙求出這個值以驗證聰聰的答案是否正確。

輸入輸出格式

輸入格式:

輸入的第1行包含1個正整數n。後面n-1行,每行3個整數x、y、w,表示x號點和y號點之間有一條邊,上面的數是w。

輸出格式:

以即約分數形式輸出這個機率(即「a/b」的形式,其中a和b必須互質。若是機率爲1,輸出「1/1」)。

輸入輸出樣例

輸入樣例#1:  複製
5
1 2 1
1 3 2
1 4 1
2 5 3
輸出樣例#1:  複製
13/25

說明

【樣例說明】

13組點對分別是(1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3) (4,4) (5,2) (5,3) (5,5)。

【數據規模】

對於100%的數據,n<=20000。

 

很明顯,思路就是統計長度爲$3$的倍數的路徑的條數,而後除以路徑總和就是答案

先貼一句話題解:先用點分計算出路徑長度,把路徑長度對$3$取模,而後用$sum[1],sum[2],sum[0]$表示模數是$1,2,3$的狀況的總數,那麼就是$ans+=sum[1]*sum[2]*2+sum[0]*sum[0]$,最後答案就是$ans/(n*n)$

用人話說的話,咱們能夠先考慮一個點,用$sum[1,2,3]$分別表示從以這一個點爲根,往下的長度對$3$取模餘數是$1,2,3$的路徑條數,那麼全部通過這一個點的路徑有多少條呢?全部長度爲$1$和$2$的路徑能夠兩兩拼起來成爲一條,反着也能夠,長度爲$3$的路徑能夠兩兩拼。因此答案就加上上面那個式子

而後進行點分,不斷遞歸就能夠了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #define ll long long
 5 #define inf 0x3f3f3f3f
 6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 7 char buf[1<<21],*p1=buf,*p2=buf;
 8 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
 9 inline int read(){
10     #define num ch-'0'
11     char ch;bool flag=0;int res;
12     while(!isdigit(ch=getc()))
13     (ch=='-')&&(flag=true);
14     for(res=num;isdigit(ch=getc());res=res*10+num);
15     (flag)&&(res=-res);
16     #undef num
17     return res;
18 }
19 char sr[1<<21],z[20];int C=-1,Z;
20 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
21 inline void print(int x){
22     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
23     while(z[++Z]=x%10+48,x/=10);
24     while(sr[++C]=z[Z],--Z);
25 }
26 const int N=20005,mod=3;
27 int head[N],Next[N<<1],edge[N<<1],ver[N<<1];ll ans=0;
28 int sz[N],son[N],sum[4],vis[N];
29 int size,mx,rt,n,tot;
30 inline void add(int u,int v,int e){
31     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
32     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e;
33 }
34 void getrt(int u,int fa){
35     sz[u]=1,son[u]=0;
36     for(int i=head[u];i;i=Next[i]){
37         int v=ver[i];
38         if(vis[v]||v==fa) continue;
39         getrt(v,u);
40         sz[u]+=sz[v];
41         cmax(son[u],sz[v]);
42     }
43     cmax(son[u],size-sz[u]);
44     if(son[u]<mx) mx=son[u],rt=u;
45 }
46 void query(int u,int fa,int d){
47     ++sum[d%mod];
48     for(int i=head[u];i;i=Next[i]){
49         int v=ver[i];
50         if(vis[v]||v==fa) continue;
51         query(v,u,(d+edge[i])%mod);
52     }
53 }
54 ll solve(int rt,int d){
55     sum[0]=sum[1]=sum[2]=0;
56     query(rt,0,d);
57     ll res=1ll*sum[1]*sum[2]*2+1ll*sum[0]*sum[0];
58     return res;
59 }
60 void divide(int u){
61     ans+=solve(u,0);
62     vis[u]=1;
63     for(int i=head[u];i;i=Next[i]){
64         int v=ver[i];
65         if(vis[v]) continue;
66         ans-=solve(v,edge[i]);
67         mx=inf,rt=0,size=sz[v];
68         getrt(v,0);
69         divide(rt);
70     }
71 }
72 inline ll gcd(ll a,ll b){
73     while(b^=a^=b^=a%=b);
74     return a;
75 }
76 int main(){
77     n=read();
78     for(int i=1;i<n;++i){
79         int u=read(),v=read(),e=read();
80         add(u,v,e%3);
81     }
82     mx=inf,size=n,ans=0,rt=0;
83     getrt(1,0),divide(rt);
84     ll p=n*n,GCD=gcd(ans,p);
85     print(ans/GCD),sr[++C]='/',print(p/GCD);
86     Ot();
87     return 0;
88 }
聰聰可可

洛谷P3806 【模板】點分治1

題目描述

給定一棵有n個點的樹

詢問樹上距離爲k的點對是否存在。

輸入輸出格式

輸入格式:

 

n,m 接下來n-1條邊a,b,c描述a到b有一條長度爲c的路徑

接下來m行每行詢問一個K

 

輸出格式:

 

對於每一個K每行輸出一個答案,存在輸出「AYE」,不然輸出」NAY」(不包含引號)

 

輸入輸出樣例

輸入樣例#1:  複製
2 1
1 2 2
2
輸出樣例#1:  複製
AYE

說明

對於30%的數據n<=100

對於60%的數據n<=1000,m<=50

對於100%的數據n<=10000,m<=100,c<=1000,K<=10000000

 

考慮一下$k$的範圍,乾脆預處理出答案而後直接$O(1)$回答詢問吧……

就是把每個節點向下長度爲$d$的路徑有多少條記下來,而後兩兩合併,時間複雜度$O(n^2)$,不知道爲毛能過……

//minamoto
#include<cstdio>
#include<iostream>
#define inf 0x3f3f3f3f
#define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
char buf[1<<21],*p1=buf,*p2=buf;
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
inline int read(){
    #define num ch-'0'
    char ch;bool flag=0;int res;
    while(!isdigit(ch=getc()))
    (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getc());res=res*10+num);
    (flag)&&(res=-res);
    #undef num
    return res;
}
const int N=10005;
int ans[10000005];
int ver[N<<1],head[N],Next[N<<1],edge[N<<1];
int sz[N],son[N],st[N];bool vis[N];
int n,m,size,mx,rt,tot,top;
inline void add(int u,int v,int e){
    ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
    ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e;
}
void getrt(int u,int fa){
    sz[u]=1,son[u]=0;
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(vis[v]||v==fa) continue;
        getrt(v,u);
        sz[u]+=sz[v],cmax(son[u],sz[v]);
    }
    cmax(son[u],size-sz[u]);
    if(son[u]<mx) mx=son[u],rt=u;
}
void query(int u,int fa,int d){
    st[++top]=d;
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(vis[v]||v==fa) continue;
        query(v,u,d+edge[i]);
    }
}
void solve(int rt,int d,int f){
    top=0;
    query(rt,0,d);
    if(f){
        for(int i=1;i<top;++i)
        for(int j=i+1;j<=top;++j)
        ++ans[st[i]+st[j]];
    }
    else{
        for(int i=1;i<top;++i)
        for(int j=i+1;j<=top;++j)
        --ans[st[i]+st[j]];
    }
}
void divide(int u){
    vis[u]=true;
    solve(u,0,1);
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(vis[v]) continue;
        solve(v,edge[i],0);
        mx=inf,rt=0,size=sz[v];
        getrt(v,0);
        divide(rt);
    }
}
int main(){
    n=read(),m=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read(),e=read();
        add(u,v,e);
    }
    rt=0,mx=inf,size=n;
    getrt(1,0),divide(rt);
    while(m--){
        int k=read();
        puts(ans[k]?"AYE":"NAY");
    }
    return 0;
}
點分治【模板】

而後就是關於點分的一些題單了,基本我都寫(chao)了題解的

洛谷P2664 樹上游戲(點分治)------------->蒟蒻的題解

 [IOI2011]Race (點分治)(洛谷地址)------------->蒟蒻的題解

動態點分治

  通常來講,點分治只能處理靜態的問題

  然而若是題目要求待修改怎麼辦哩?(固然是修改一次作一次點分,多省事)

  這個時候就須要動態點分治大顯身手啦

  咱們能夠對樹中的每個點維護一個數組或者數據結構,而後經過維護它們來完成狀態之間的轉移

  然而一條鏈就能夠送咱們上天……

  咱們考慮一下,整棵樹的結構是不變的(若是變了那就是LCT的範圍了),被修改的只有點權。那麼,咱們每一次進行點分時選到的重心也是不變的。

  下面給出點分樹的定義:把點分治時每一層的重心之間連邊,這就構成了一顆高度爲$logn$的新樹,咱們叫它分治樹。

  用人話說的話,就是咱們每一次點分完一棵樹,會繼續往子樹裏點分,那麼咱們能夠把這一次的重心和子樹裏的重心連邊。由於點分的遞歸層數最多隻有$logn$層,因此這棵樹高度爲$logn$

  八成仍是沒解釋清楚(畢竟我不太對的起語文老師),來畫個圖吧,這是一棵樹,重心是點$1$

  咱們假設已經處理完了全部通過點$1$的路徑,而後遞歸進子樹繼續點分,那麼實際上原樹被拆成了這麼兩棵樹,兩個重心分別爲$2$和$6$

  那麼把第一層的重心和第二層的重心給鏈接起來(用紅色表示)

  而後咱們繼續進行點分,咱們已經把通過點$2$和點$6$的全部路徑都已經處理完了,那麼子樹又會繼續拆分

  而後由於子樹大小隻有$1$,重心就是他們本身,繼續和上一層的重心連邊

  而後這一棵點分樹就建好了

  貼代碼(事實上只是在原先的板子上加了一句話而已,就是那句 fa[rt]=u )

  那麼每一次修改,只要在點分樹裏不斷往上跳,就可以維護整棵樹的信息了

void solve(int u){
    vis[u]=true;int totsz=size;
    for(int i=head[u];i;i=Next[i]){
        int v=ver[i];
        if(vis[v]) continue;
        rt=0,son[0]=n+1;
        size=sz[v]>sz[u]?totsz-sz[u]:sz[v];
        findrt(v,0),fa[rt]=u,solve(rt);
        //事實上,咱們只須要記錄點分樹上的父親便可(通常狀況下) 
    }
}

  而後基本的介紹就到這裏……彷佛仍是太抽象了……仍是來說幾道題目吧

「BZOJ1095」[ZJOI2007] Hide 捉迷藏

題目描述

Jiajia和Wind是一對恩愛的夫妻,而且他們有不少孩子。某天,Jiajia、Wind和孩子們決定在家裏玩捉迷藏遊戲。他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,這N-1條走廊的分佈使得任意兩個屋子都互相可達。

遊戲是這樣進行的,孩子們負責躲藏,Jiajia負責找,而Wind負責操縱這N個屋子的燈。在起初的時候,全部的燈都沒有被打開。每一次,孩子們只會躲藏在沒有開燈的房間中,可是爲了增長刺激性,孩子們會要求打開某個房間的電燈或者關閉某個房間的電燈。爲了評估某一次遊戲的複雜性,Jiajia但願知道可能的最遠的兩個孩子的距離(即最遠的兩個關燈房間的距離)。

咱們將以以下形式定義每一種操做:

  • C(hange) i 改變第i個房間的照明狀態,若原來打開,則關閉;若原來關閉,則打開。
  • G(ame) 開始一次遊戲,查詢最遠的兩個關燈房間的距離。

輸入輸出格式

輸入格式:

第一行包含一個整數N,表示房間的個數,房間將被編號爲1,2,3…N的整數。

接下來N-1行每行兩個整數a, b,表示房間a與房間b之間有一條走廊相連。

接下來一行包含一個整數Q,表示操做次數。接着Q行,每行一個操做,如上文所示。

輸出格式:

對於每個操做Game,輸出一個非負整數到hide.out,表示最遠的兩個關燈房間的距離。若只有一個房間是關着燈的,輸出0;若全部房間的燈都開着,輸出-1。

輸入輸出樣例

輸入樣例#1:
8
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G
輸出樣例#1:
4
3
3
4

說明

對於20%的數據, N ≤50, M ≤100;

對於60%的數據, N ≤3000, M ≤10000; 對於100%的數據, N ≤100000, M ≤500000。

 

動態點分治的板子題

(然而我該說我當初作這道題是抄了島娘和hzwer的括號序列麼……由於看着以爲代碼比較好抄……因此根本沒打過動態點分的板子……)

我就直接說一下思路(而後隨便放個別的大佬的代碼好了)

咱們考慮一下,在每個點須要維護這一個點在點分樹中往下延伸到黑點的全部長度,而後每一次都把最長的和次長的給鏈接起來

這個能夠每個節點開一個大根堆

然而若是兩條鏈是來自同一棵子樹的就GG了……

那麼能夠再開一個堆,維護這一個點的全部兒子的堆頂,而後每一次取出的最長和次長能夠保證不是在同一棵子樹裏

而後再開一個堆維護一下每個節點的答案……

考慮修改怎麼操做?每一次在第一個堆裏進行修改,維護第二個堆和第三個堆就能夠了

分治樹深度$logn$,堆操做時間複雜度是$logn$,總時間複雜度是$O(nlog^2n)$

而後放一下LadyLex大佬的代碼吧(纔不是由於我懶得打呢)

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <ctime>
  4 #include <set>
  5 #include <queue>
  6 using namespace std;
  7 #define N 100010
  8 #define inf 0x3fffffff
  9 #define Vt Vater[rt]
 10 int n,e,adj[N];
 11 struct edge{int zhong,next;}s[N<<1];
 12 inline void add(int qi,int zhong)
 13     {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;}
 14 int Vater[N],size[N],root,totsize,maxs[N];
 15 bool state[N],vis[N];
 16 #define max(a,b) ((a)>(b)?(a):(b))
 17 #define min(a,b) ((a)<(b)?(a):(b))
 18 struct heap
 19 {
 20     priority_queue<int>q1,q2;
 21     inline void push(int x){q1.push(x);}
 22     inline void erase(int x){q2.push(x);}
 23     inline int top()
 24     {
 25         while(q2.size()&&q1.top()==q2.top())q1.pop(),q2.pop();
 26         return q1.top();
 27     }
 28     inline void pop()
 29     {
 30         while(q2.size()&&q1.top()==q2.top())q1.pop(),q2.pop();
 31         q1.pop();
 32     }
 33     inline int top2()
 34     {
 35         int val=top();pop();
 36         int ret=top();push(val);
 37         return ret;
 38     }
 39     inline int size()
 40     {
 41         return q1.size()-q2.size();
 42     }
 43 }h1[N],h2[N],h3;
 44 inline void dfs1(int rt,int fa)
 45 {
 46     size[rt]=1,maxs[rt]=0;
 47     for(int i=adj[rt];i;i=s[i].next)
 48         if(s[i].zhong!=fa&&!vis[s[i].zhong])
 49             dfs1(s[i].zhong,rt),size[rt]+=size[s[i].zhong],
 50             maxs[rt]=max(maxs[rt],size[s[i].zhong]);
 51     maxs[rt]=max(maxs[rt],totsize-maxs[rt]);
 52     if(maxs[rt]<maxs[root])root=rt;
 53 }
 54 int f[N][18],bin[25],tp,deep[N];
 55 inline void pre(int rt,int fa)
 56 {
 57     f[rt][0]=fa;deep[rt]=deep[fa]+1;
 58     for(int i=1;bin[i]+1<=deep[rt];++i)f[rt][i]=f[f[rt][i-1]][i-1];
 59     for(int i=adj[rt];i;i=s[i].next)
 60         if(s[i].zhong!=fa)pre(s[i].zhong,rt);
 61 }
 62 inline int LCA(int a,int b)
 63 {
 64     if(deep[a]<deep[b])a^=b,b^=a,a^=b;
 65     int i,cha=deep[a]-deep[b];
 66     for(i=tp;~i;--i)if(cha&bin[i])a=f[a][i];
 67     if(a==b)return a;
 68     for(i=tp;~i;--i)
 69         if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
 70     return f[a][0];
 71 }
 72 inline int dis(int a,int b)
 73     {return deep[a]+deep[b]-(deep[LCA(a,b)]<<1);}
 74 inline void dfs3(int rt,int fa,int Vatty)
 75 {
 76     h1[root].push(dis(rt,Vatty));
 77     for(int i=adj[rt];i;i=s[i].next)
 78         if(!vis[s[i].zhong]&&s[i].zhong!=fa)
 79             dfs3(s[i].zhong,rt,Vatty);
 80 }
 81 inline void dfs2(int rt,int fa)
 82 {
 83     Vt=fa,vis[rt]=1,h2[rt].push(0);
 84     int siz=totsize;
 85     for(int i=adj[rt];i;i=s[i].next)
 86         if(!vis[s[i].zhong])
 87         {
 88             if(size[s[i].zhong]>size[rt])
 89                 totsize=siz-size[rt];
 90             else 
 91                 totsize=size[s[i].zhong];
 92             root=0,dfs1(s[i].zhong,0),dfs3(root,0,rt);
 93             h2[rt].push(h1[root].top()),dfs2(root,rt);
 94         }
 95     if(h2[rt].size()>1)h3.push(h2[rt].top()+h2[rt].top2());
 96 }
 97 inline void turnoff(int who)
 98 {
 99     int val,tmp;
100     if(h2[who].size()>1)h3.erase(h2[who].top()+h2[who].top2());
101     h2[who].push(0);
102     if(h2[who].size()>1)h3.push(h2[who].top()+h2[who].top2());
103     //queue empty() 後依然有數
104     for(int rt=who;Vt;rt=Vt)
105     {
106         if(h2[Vt].size()>1)h3.erase(h2[Vt].top()+h2[Vt].top2());
107         if(h1[rt].size())h2[Vt].erase(h1[rt].top());
108         h1[rt].push(dis(who,Vt));
109         h2[Vt].push(h1[rt].top());
110         if(h2[Vt].size()>1)h3.push(h2[Vt].top()+h2[Vt].top2());
111     }
112 }
113 inline void turnon(int who)
114 {
115     int val,tmp;
116     if(h2[who].size()>1)h3.erase(h2[who].top()+h2[who].top2());
117     h2[who].erase(0);
118     if(h2[who].size()>1)h3.push(h2[who].top()+h2[who].top2());
119     //queue empty()後依然有數
120     for(int rt=who;Vt;rt=Vt)
121     {
122         if(h2[Vt].size()>1)h3.erase(h2[Vt].top()+h2[Vt].top2());
123         h2[Vt].erase(h1[rt].top());
124         h1[rt].erase(dis(who,Vt));
125         if(h1[rt].size())h2[Vt].push(h1[rt].top());
126         if(h2[Vt].size()>1)h3.push(h2[Vt].top()+h2[Vt].top2());
127     }
128 }
129 char B[1<<15],X=0,*S=B,*T=B;
130 #define getc ( S==T&&( T=(S=B)+fread(B,1,1<<15,stdin),S==T )?0:*S++ )
131 inline int read()
132 {
133     int x=0;while(X<'0'||X>'9')X=getc;
134     while(X>='0'&&X<='9')x=10*x+(X^48),X=getc;
135     return x;
136 }
137 inline void readc(){X=getc;while(X<'A'||X>'Z')X=getc;}
138 int main()
139 {
140     // freopen("hide1.in","r",stdin);
141     // freopen("hide.out","w",stdout);
142     n=read();
143     register int i,j,q,a,b,cnt=n;
144     for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1;
145     while(bin[tp+1]<=n)++tp;
146     for(i=1;i<n;++i)
147         a=read(),b=read(),add(a,b),add(b,a);
148     pre(1,0);
149     maxs[0]=inf,root=0,totsize=n,dfs1(1,0),dfs2(root,0);
150     q=read();
151     while(q--)
152     {
153         readc();
154         if(X=='C')
155         {
156             i=read();
157             if(state[i])++cnt,turnoff(i);
158             else --cnt,turnon(i);
159             state[i]^=1;
160         }
161         else
162         {
163             if(cnt<2)printf("%d\n",cnt-1);
164             else printf("%d\n",h3.top());
165         }
166     }
167 }
捉迷藏

BZOJ 3924: [Zjoi2015]幻想鄉戰略遊戲(動態點分治)

題目描述

傲嬌少女幽香正在玩一個很是有趣的戰略類遊戲,原本這個遊戲的地圖其實還不算太大,幽香還能管得過來,可是不知道爲何如今的網遊廠商把遊戲的地圖越作越大,以致於幽香一眼根本看不過來,更別說和別人打仗了。

在打仗以前,幽香如今面臨一個很是基本的管理問題須要解決。 整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊鏈接起來,使得每兩個點之間有一條惟一的路徑將它們鏈接起來。

在遊戲中,幽香可能在空地上增長或者減小一些軍隊。同時,幽香能夠在一個空地上放置一個補給站。 若是補給站在點u上,而且空地v上有dv個單位的軍隊,那麼幽香天天就要花費dv*dist(u,v)的金錢來補給這些軍隊。

因爲幽香須要補給全部的軍隊,所以幽香總共就要花費爲Sigma(Dv*dist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(惟一路徑的權和)。

由於遊戲的規定,幽香只能選擇一個空地做爲補給站。在遊戲的過程當中,幽香可能會在某些空地上製造一些軍隊,也可能會減小某些空地上的軍隊,進行了這樣的操做之後,出於經濟上的考慮,幽香每每能夠移動他的補給站從而省一些錢。

可是因爲這個遊戲的地圖是在太大了,幽香沒法輕易的進行最優的安排,你能幫幫她嗎? 你能夠假定一開始全部空地上都沒有軍隊。

輸入輸出格式

輸入格式:

第一行兩個數n和Q分別表示樹的點數和幽香操做的個數,其中點從1到n標號。 接下來n-1行,每行三個正整數a,b,c,表示a和b之間有一條邊權爲c的邊。 接下來Q行,每行兩個數u,e,表示幽香在點u上放了e單位個軍隊(若是e<0,就至關因而幽香在u上減小了|e|單位個軍隊,說白了就是du←du+e)。數據保證任什麼時候刻每一個點上的軍隊數量都是非負的。

輸出格式:

對於幽香的每一個操做,輸出操做完成之後,天天的最小花費,也即若是幽香選擇最優的補給點進行補給時的花費。

輸入輸出樣例

輸入樣例#1:  複製
10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 6 1
2 7 1
5 8 1
7 9 1
1 10 1
3 1
2 1
8 1
3 1
4 1
輸出樣例#1:  複製
0
1
4
5
6

說明

對於全部數據,1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5 很是神奇的是,對於全部數據,這棵樹上的點的度數都不超過20,且N,Q>=1

 

咱們具體來分析一下這道題目咱們要維護什麼

題目要求使$\sum d_v*dis(u,v)$最小,其中$d_v$爲$v$點的點權,$dis(u,v)$爲原樹中$u,v$兩點的距離。題目要求就是求帶權重心。假設咱們當前已經選定了點$v$爲答案,那麼考慮它的一個子節點$w$,若是把補給站從點$v$轉移到$w$,那麼$w$的全部子樹內的點到補給站的距離少了$dis(v,w)$,而其餘全部點到補給站的距離多了$dis(v,w)$。咱們假設$sum_v[v]$爲$v$的子樹內的點權和,那麼答案總共的變化量是$$dis(u,v)*(sum_v[v]-sum_v[w]-sum_v[w])$$

不難發現,當$sum_v[w]*2>sum_v[v]$的時候,答案的變化量是小於零的,也就是說代價減少,能夠變得更優。並且,知足這樣條件的點$w$最多隻有一個

那麼咱們能夠每一次選定一個點,而後看看往他的哪一個子樹走更優,若是沒有說明他本身就已是最優的了。這樣不斷下去確定能找到答案。

可是因爲原圖多是一條鏈,要怎麼作才能保證複雜度呢?咱們選擇在點分樹上走,每一次都跳到它下一層的重心,這樣能夠保證層數最多隻有$O(log n)$

而後考慮如何維護答案。不難發現,對於點分樹上一個點$v$,它的子樹就是在點分治時它被選爲重心時的那棵樹。考慮點分樹上的一對父子$u,v$,咱們設$sum_a[v]$表示$v$的子樹內的全部點到他的代價之和,$sum_b[v]$爲$v$的子樹內的全部點到$v$點父親(也就是點$u$)的距離之和,$sum_v[v]$仍是表示子樹的點權之和。那麼咱們設答案已經選定爲點$w$,而且已經把$v$這一整棵子樹上全部點到點$w$的距離之和計算完畢,那麼考慮要加上$v$在點分樹上的父親$u$,要計算的答案只有$u$的不包含$v$的子樹,答案是$sum_a[v]+sum_a[u]-sum_b[v]+sum_v[u]*dis(u,v)$。因而只要在點分樹上不斷找父親併合並,就能夠知道答案了

而後咱們只要能在修改時維護好這三個數組就能夠了

至於修改時如何維護呢?咱們修改一個點以後,而後不斷在點分樹上往父節點跳,每一次只會對它的祖先節點的這些值產生影響,一個一個修改便可

ps:還有個小細節,由於咱們判斷是否更優要在原樹上走,而後計算答案要在點分樹裏走,因此對於每個節點不只要記錄它的父親,還要記錄它往下能走哪幾條邊,在原樹的邊判斷是否更優,而後往點分樹上走來保證計算答案的複雜度

而後又新學會了一招,用$RMQ O(1)$查詢$LCA$(只會倍增和樹剖的我瑟瑟發抖),總時間複雜度$O(nlog^2n)$

  1 //minamoto
  2 #include<cstdio>
  3 #include<iostream>
  4 #include<cstring>
  5 #define ll long long
  6 #define N 100005
  7 #define inf 0x3f3f3f3f
  8 #define rint register int
  9 using namespace std;
 10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 11 char buf[1<<21],*p1=buf,*p2=buf;
 12 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
 13 inline int read(){
 14     #define num ch-'0'
 15     char ch;bool flag=0;int res;
 16     while(!isdigit(ch=getc()))
 17     (ch=='-')&&(flag=true);
 18     for(res=num;isdigit(ch=getc());res=res*10+num);
 19     (flag)&&(res=-res);
 20     #undef num
 21     return res;
 22 }
 23 char sr[1<<21],z[20];int C=-1,Z;
 24 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
 25 inline void print(ll x){
 26     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
 27     while(z[++Z]=x%10+48,x/=10);
 28     while(sr[++C]=z[Z],--Z);sr[++C]='\n';
 29 }
 30 struct G{
 31     int head[N],Next[N<<1],edge[N<<1],ver[N<<1],tot;
 32     G(){tot=0;memset(head,0,sizeof(head));}
 33     inline void add(int u,int v,int e){
 34         ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
 35     }
 36 }T1,T2;
 37 int n,q,st[N<<1][18],logn[N<<1],bin[25],tp;
 38 ll sum,ans,d[N],dis1[N],dis2[N],sumv[N];
 39 int dfn[N],num;
 40 void dfs1(int u,int fa){
 41     st[dfn[u]=++num][0]=d[u];
 42     for(int i=T1.head[u];i;i=T1.Next[i]){
 43         int v=T1.ver[i];
 44         if(v==fa) continue;
 45         d[v]=d[u]+T1.edge[i],dfs1(v,u),st[++num][0]=d[u];
 46     }
 47 }
 48 inline ll LCA(int a,int b){
 49     if(dfn[a]>dfn[b]) a^=b^=a^=b;
 50     int k=logn[dfn[b]-dfn[a]+1];
 51     return min(st[dfn[a]][k],st[dfn[b]-bin[k]+1][k])<<1;
 52 }
 53 inline ll dis(int a,int b){return d[a]+d[b]-LCA(a,b);}
 54 int sz[N],son[N],size,rt,fa[N];bool vis[N];
 55 void dfs2(int u,int fa){
 56     sz[u]=1,son[u]=0;
 57     for(int i=T1.head[u];i;i=T1.Next[i]){
 58         int v=T1.ver[i];
 59         if(vis[v]||v==fa) continue;
 60         dfs2(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
 61     }
 62     cmax(son[u],size-sz[u]);
 63     if(son[u]<son[rt]) rt=u;
 64 }
 65 void dfs3(int u){
 66     vis[u]=true;
 67     for(int i=T1.head[u];i;i=T1.Next[i]){
 68         int v=T1.ver[i];
 69         if(vis[v]) continue;
 70         rt=0,size=sz[v],son[0]=n+1;
 71         dfs2(v,0),T2.add(u,rt,v),fa[rt]=u,dfs3(rt);
 72     }
 73 }
 74 inline void update(int u,int val){
 75     sumv[u]+=val;
 76     for(int p=u;fa[p];p=fa[p]){
 77         ll dist=dis(fa[p],u)*val;
 78         dis1[fa[p]]+=dist;
 79         dis2[p]+=dist;
 80         sumv[fa[p]]+=val;
 81     }
 82 }
 83 inline ll calc(int u){
 84     ll ans=dis1[u];
 85     for(int p=u;fa[p];p=fa[p]){
 86         ll dist=dis(fa[p],u);
 87         ans+=dis1[fa[p]]-dis2[p];
 88         ans+=dist*(sumv[fa[p]]-sumv[p]);
 89     }
 90     return ans;
 91 }
 92 ll query(int u){
 93     ll ans=calc(u);
 94     for(int i=T2.head[u];i;i=T2.Next[i]){
 95         ll tmp=calc(T2.edge[i]);
 96         if(tmp<ans) return query(T2.ver[i]);
 97     }
 98     return ans;
 99 }
100 void init(){
101     n=read(),q=read();
102     bin[0]=1,logn[0]=-1;
103     for(rint i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
104     while(bin[tp+1]<=(n<<1)) ++tp;
105     for(rint i=1;i<=(n<<1);++i) logn[i]=logn[i>>1]+1;
106     for(rint i=1;i<n;++i){
107         rint u=read(),v=read(),e=read();
108         T1.add(u,v,e),T1.add(v,u,e);
109     }
110     dfs1(1,0),rt=0,son[0]=n+1,size=n,dfs2(1,0);
111     for(rint j=1;j<=tp;++j)
112     for(rint i=1;i+bin[j]-1<=(n<<1);++i)
113     st[i][j]=min(st[i][j-1],st[i+bin[j-1]][j-1]);
114 }
115 int main(){
116     init();
117     int LastOrder=rt;dfs3(rt);
118     while(q--){
119         int x=read(),y=read();update(x,y);
120         print(query(LastOrder));
121     }
122     Ot();
123     return 0;
124 }
幻想鄉戰略遊戲

BZOJ4012 [HNOI2015]開店 (動態點分治)

Description

 風見幽香有一個好朋友叫八雲紫,她們常常一塊兒看星星看月亮從詩詞歌賦談到

人生哲學。最近她們靈機一動,打算在幻想鄉開一家小店來作生意賺點錢。這樣的
想法固然很是好啦,可是她們也發現她們面臨着一個問題,那就是店開在哪裏,面
向什麼樣的人羣。很神奇的是,幻想鄉的地圖是一個樹形結構,幻想鄉一共有 n
個地方,編號爲 1 到 n,被 n-1 條帶權的邊鏈接起來。每一個地方都住着一個妖怪,
其中第 i 個地方的妖怪年齡是 x_i。妖怪都是些比較喜歡安靜的傢伙,因此它們並
不但願和不少妖怪相鄰。因此這個樹全部頂點的度數都小於或等於 3。妖怪和人一
樣,興趣點隨着年齡的變化天然就會變化,好比咱們的 18 歲少女幽香和八雲紫就
比較喜歡可愛的東西。幽香經過研究發現,基本上妖怪的興趣只跟年齡有關,因此
幽香打算選擇一個地方 u(u爲編號),而後在 u開一家面向年齡在 L到R 之間(即
年齡大於等於 L、小於等於 R)的妖怪的店。也有可能 u這個地方離這些妖怪比較
遠,因而幽香就想要知道全部年齡在 L 到 R 之間的妖怪,到點 u 的距離的和是多
少(妖怪到 u 的距離是該妖怪所在地方到 u 的路徑上的邊的權之和) ,幽香把這個
稱爲這個開店方案的方便值。幽香她們尚未決定要把店開在哪裏,八雲紫卻是準
備了不少方案,因而幽香想要知道,對於每一個方案,方便值是多少呢。

Input

 第一行三個用空格分開的數 n、Q和A,表示樹的大小、開店的方案個數和妖

怪的年齡上限。 
第二行n個用空格分開的數 x_一、x_二、…、x_n,x_i 表示第i 個地點妖怪的年
齡,知足0<=x_i<A。(年齡是能夠爲 0的,例如剛出生的妖怪的年齡爲 0。) 
接下來 n-1 行,每行三個用空格分開的數 a、b、c,表示樹上的頂點 a 和 b 之
間有一條權爲c(1 <= c <= 1000)的邊,a和b 是頂點編號。 
接下來Q行,每行三個用空格分開的數 u、 a、 b。對於這 Q行的每一行,用 a、
b、A計算出 L和R,表示詢問「在地方 u開店,面向妖怪的年齡區間爲[L,R]的方
案的方便值是多少」。對於其中第 1 行,L 和 R 的計算方法爲:L=min(a%A,b%A), 
R=max(a%A,b%A)。對於第 2到第 Q行,假設前一行獲得的方便值爲 ans,那麼當
前行的 L 和 R 計算方法爲: L=min((a+ans)%A,(b+ans)%A), 
R=max((a+ans)%A,(b+ans)%A)。 

Output

對於每一個方案,輸出一行表示方便值。 

Sample Input

10 10 10 
0 0 7 2 1 4 7 7 7 9 
1 2 270 
2 3 217 
1 4 326 
2 5 361 
4 6 116 
3 7 38 
1 8 800 
6 9 210 
7 10 278 
8 9 8 
2 8 0 
9 3 1 
8 0 8 
4 2 7 
9 7 3 
4 7 0 
2 2 7 
3 2 1 
2 3 4

Sample Output

1603 
957 
7161 
9466 
3232 
5223 
1879 
1669 
1282 
0

HINT

 知足 n<=150000,Q<=200000。對於全部數據,知足 A<=10^9

 

咱們考慮對於每個點分樹上的點維護什麼。咱們記錄三個值,$sz_0$表示子樹內的點數之和,$sz_1$表示子樹內全部點到其的距離之和,$sz_2$表示子樹內全部點到其父親的距離之和。那麼考慮咱們在跳點分樹的時候要如何維護答案呢?很明顯$sz_1[u]+\sum sz_1[fa]-sz_2[p]+(sz_0[fa]-sz_0[p])*dist(fa,u)$就是在點分樹上與$u$的$LCA$是$fa$的全部點的距離之和,其中$fa$爲$p$的父親,$p$爲從$u$不斷跳父親直到根,那麼只要不斷枚舉$fa$,並不斷往上跳並維護答案就能夠了。

然而上面只是爲了方便理解,由於具體的計算不是這樣的。咱們對於$u$點內部的貢獻能夠直接計算,而後考慮往上跳。在上述的式子中若是一個點$p$有$fa$,那麼答案就要減去$sz_0[p]*dist(fa,u)+sz_2[p]$,若是一個點不是$u$那麼答案就要加上$sz_0[p]+dist(p,u)$,而後每個點都要加上本身的$sz_1$。按這個規律在跳點分樹的時候不斷加就行了

而後考慮怎麼在$[l,r]$以內,很明顯能夠搞一個差分,把每個節點在點分樹上的子樹內的全部年齡的距離之和加起來,再作前綴和,那麼在年齡$[l,r]$的人數就是$[1,r]-[1,l-1]$(由於實際上存儲時不可能按年齡,因此能夠在每個點開一個vector而後排序一下便可)

  1 //minamoto
  2 #include<cstdio>
  3 #include<iostream>
  4 #include<cstring>
  5 #include<vector>
  6 #include<algorithm>
  7 #define ll long long
  8 #define N 150005
  9 using namespace std;
 10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 11 char buf[1<<21],*p1=buf,*p2=buf;
 12 template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
 13 inline int read(){
 14     #define num ch-'0'
 15     char ch;bool flag=0;int res;
 16     while(!isdigit(ch=getc()))
 17     (ch=='-')&&(flag=true);
 18     for(res=num;isdigit(ch=getc());res=res*10+num);
 19     (flag)&&(res=-res);
 20     #undef num
 21     return res;
 22 }
 23 char sr[1<<21],z[20];int C=-1,Z;
 24 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
 25 inline void print(ll x){
 26     if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;
 27     while(z[++Z]=x%10+48,x/=10);
 28     while(sr[++C]=z[Z],--Z);sr[++C]='\n';
 29 }
 30 int head[N],Next[N<<1],ver[N<<1],edge[N<<1];
 31 int n,tot,val[N],q,maxn;
 32 int st[N<<1][19],d[N],dfn[N],num,bin[25],tp,logn[N<<1];
 33 inline void add(int u,int v,int e){
 34     ver[++tot]=v,Next[tot]=head[u],head[u]=tot,edge[tot]=e;
 35     ver[++tot]=u,Next[tot]=head[v],head[v]=tot,edge[tot]=e;
 36 }
 37 inline void ST(){
 38     for(int j=1;j<=tp;++j)
 39     for(int i=1;i+bin[j]-1<=(n<<1);++i)
 40     st[i][j]=min(st[i][j-1],st[i+bin[j-1]][j-1]);
 41 }
 42 void dfs1(int u,int fa){
 43     st[dfn[u]=++num][0]=d[u];
 44     for(int i=head[u];i;i=Next[i]){
 45         int v=ver[i];
 46         if(v==fa) continue;
 47         d[v]=d[u]+edge[i],dfs1(v,u),st[++num][0]=d[u];
 48     }
 49 }
 50 int fa[N],sz[N],son[N],size,rt;bool vis[N];
 51 void dfs2(int u,int fa){
 52     sz[u]=1,son[u]=0;
 53     for(int i=head[u];i;i=Next[i]){
 54         int v=ver[i];
 55         if(vis[v]||v==fa) continue;
 56         dfs2(v,u),sz[u]+=sz[v],cmax(son[u],sz[v]);
 57     }
 58     cmax(son[u],size-sz[u]);
 59     if(son[u]<son[rt]) rt=u;
 60 }
 61 inline ll dis(int a,int b){
 62     if(dfn[a]>dfn[b]) a^=b^=a^=b;
 63     int k=logn[dfn[b]-dfn[a]+1];
 64     return d[a]+d[b]-(min(st[dfn[a]][k],st[dfn[b]-bin[k]+1][k])<<1);
 65 }
 66 struct node{
 67     int val;ll sz[3];
 68     node(int a=0,ll b=0,ll c=0,ll d=0){val=a,sz[0]=b,sz[1]=c,sz[2]=d;}
 69     inline bool operator <(const node &b)const
 70     {return val<b.val;}
 71 };
 72 vector<node> sta[N];
 73 void dfs3(int u,int f,int rt){
 74     sta[rt].push_back(node(val[u],1,dis(u,rt),fa[rt]?dis(u,fa[rt]):0));
 75     for(int i=head[u];i;i=Next[i]){
 76         int v=ver[i];
 77         if(v==f||vis[v]) continue;
 78         dfs3(v,u,rt);
 79     }
 80 }
 81 void dfs4(int u){
 82     vis[u]=true;
 83     dfs3(u,0,u);sta[u].push_back(node(-1,0,0,0));
 84     sort(sta[u].begin(),sta[u].end());
 85     for(int i=0,j=sta[u].size();i<j-1;++i)
 86     sta[u][i+1].sz[0]+=sta[u][i].sz[0],
 87     sta[u][i+1].sz[1]+=sta[u][i].sz[1],
 88     sta[u][i+1].sz[2]+=sta[u][i].sz[2];
 89     for(int i=head[u];i;i=Next[i]){
 90         int v=ver[i];
 91         if(vis[v]) continue;
 92         rt=0,size=sz[v];
 93         dfs2(v,0),fa[rt]=u,dfs4(rt);
 94     }
 95 }
 96 inline node query(int id,int l,int r){
 97     if(id==0) return node();
 98     vector<node>::iterator it1=upper_bound(sta[id].begin(),sta[id].end(),node(r,0,0,0));--it1;
 99     vector<node>::iterator it2=upper_bound(sta[id].begin(),sta[id].end(),node(l-1,0,0,0));--it2;
100     return node(0,it1->sz[0]-it2->sz[0],it1->sz[1]-it2->sz[1],it1->sz[2]-it2->sz[2]);
101 }
102 inline ll calc(int u,int l,int r){
103     ll res=0;
104     for(int p=u;p;p=fa[p]){
105         node a=query(p,l,r);
106         res+=a.sz[1];
107         if(p!=u) res+=a.sz[0]*dis(p,u);
108         if(fa[p]) res-=a.sz[2]+a.sz[0]*dis(fa[p],u);
109     }
110     return res;
111 }
112 int main(){
113     ll ans=0;
114     n=read(),q=read(),maxn=read();
115     bin[0]=1,logn[0]=-1;
116     for(int i=1;i<=20;++i) bin[i]=bin[i-1]<<1;
117     while(bin[tp+1]<=(n<<1)) ++tp;
118     for(int i=1;i<=(n<<1);++i) logn[i]=logn[i>>1]+1;
119     for(int i=1;i<=n;++i) val[i]=read();
120     for(int i=1;i<n;++i){
121         int u=read(),v=read(),e=read();
122         add(u,v,e);
123     }
124     dfs1(1,0),ST();
125     rt=0,son[0]=n+1,size=n,dfs2(1,0);
126     dfs4(rt);
127     while(q--){
128         int a=read(),b=read(),c=read();
129         b=(b+ans)%maxn,c=(c+ans)%maxn;
130         if(b>c) b^=c^=b^=c;
131         print(ans=calc(a,b,c));
132     }
133     Ot();
134     return 0;
135 }
開店

 


emm……最後再來兩個大boss吧(我已經想(抄)到了一種很棒的解法惋惜這裏寫不下)

說真的抄代碼其實很累的

洛谷P3676 小清新數據結構題--------->蒟蒻的題解

bzoj3435 [Wc2014]紫荊花之戀(權限)(非權限)---------------->蒟蒻的題解

那麼就到這裏吧……感謝觀看……累死我了……

相關文章
相關標籤/搜索