點分治&動態點分治小結

(寫篇博客證實本身還活着×2)html

轉載請註明原文地址:http://www.cnblogs.com/LadyLex/p/8006488.html node

有的時候,咱們會發現這樣一類題:它長得很像一個$O(n)$樹規,數組

可是卻很難用單獨的數組維護對應的信息,這樣咱們就有了澱粉質點分治。數據結構

經過直接統計($O(nlogn)$)或者加上數據結構(好比樹狀數組,堆,線段樹等等)維護信息($O(nlog^{2}n)$),ide

咱們能夠統計以前很差統計的東西函數

這篇博客將會介紹點分治以及動態點分治的簡單應用,但願閱讀本文的你能有所收穫,那就再好不過了。測試

一.樹規到點分治ui

咱們先來看一道簡單的題目……spa

2152: 聰聰可可

Time Limit: 3 Sec  Memory Limit: 259 MB

Description

聰聰和可但是兄弟倆,他們倆常常爲了一些雜事打起來,例如家中只剩下最後一根冰棍而兩人都想吃、兩我的都想玩兒電腦(但是他們家只有一臺電腦)……遇到這種問題,通常狀況下石頭剪刀布就行了,但是他們已經玩兒膩了這種低智商的遊戲。他們的爸爸快被他們的爭吵煩死了,因此他發明了一個新遊戲:由爸爸在紙上畫n個「點」,並用n-1條「邊」把這n個「點」剛好連通(其實這就是一棵樹)。而且每條「邊」上都有一個數。接下來由聰聰和可可分別隨即選一個點(固然他們選點時是看不到這棵樹的),若是兩個點之間全部邊上數的和加起來剛好是3的倍數,則判聰聰贏,不然可可贏。聰聰很是愛思考問題,在每次遊戲後都會仔細研究這棵樹,但願知道對於這張圖本身的獲勝機率是多少。現請你幫忙求出這個值以驗證聰聰的答案是否正確。

Input

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

Output

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

Sample Input

5
1 2 1
1 3 2
1 4 1
2 5 3

Sample Output

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的倍數%3=0(廢話……),所以咱們能夠設$f[i][0/1/2]$爲以i爲根的子樹,向下伸出的路徑中長度%3=0/1/2的路徑數。
這樣咱們在每個點簡單的合併一下(注意去掉同一子樹的貢獻)便可。
這樣的作法顯然是$O(n)$的對吧……
如今讓咱們考慮一個蠢一點的作法:
咱們依然考慮每一個點做爲路徑頂點時符合條件的點的個數,那麼咱們能夠考慮它的每一個子樹,
數一下里面每一個路徑的條數,而後合起來對吧。
對於這個節點子樹中的節點,咱們是化做子問題遞歸處理的。
那麼咱們能夠把樹換個形狀……換一個順序來遞歸處理
因爲重心有個性質:到其餘點的距離和最小,
那咱們能夠暴力數一下每種長度0/1/2的路徑有多少,再暴力加上,而後對於被這個重心分割出的幾個子樹分治遞歸解決……
這樣找重心的複雜度呢?咱們最多找$log$次重心就會只剩下一個點,所以複雜度爲$O(nlogn)$. (由於每次找到一個重心,新的子樹最可能是原來子樹大小的一半)
那麼介紹一下咱們必須的操做:
首先是初始化以及數組定義。個人習慣是這樣的:
1 int size[N],maxs[N],totsize,root;
2 bool vis[N];
3 inline void intn()
4 {
5     maxs[0]=inf,root=0,dfs1(1,0),solve(root);
6 }
其中$size[i]$表示目前以i點爲根的子樹size大小 $maxs[i]$爲i點size最大的兒子的size(這個「兒子」能夠是i點在有根樹中的父親)
$totsize$爲當前聯通塊大小 $root$爲最終的重心 $vis[i]$表示i點是否已經被選取做爲重心。vis保證咱們能夠「截取」出準確的對應聯通塊 而後是找重心的部分:
 1 inline void dfs1(int rt,int fa)
 2 {
 3     size[rt]=1,maxsize[rt]=0;
 4     for(int i=adj[rt];i;i=s[i].next)
 5         if(s[i].zhong!=fa&&!mark[s[i].zhong])
 6         {
 7             dfs1(s[i].zhong,rt),
 8             size[rt]+=size[s[i].zhong],
 9             maxsize[rt]=max(maxsize[rt],size[s[i].zhong]);
10         }
11     maxsize[rt]=max(maxsize[rt],totsize-maxsize[rt]);
12     if(maxsize[rt]<maxsize[root])root=rt;
13 }
這其實相似一個樹規的$O(n)$過程……這樣找到最後$root$就是重心 而後是solve過程:
 1 inline void solve(int rt)
 2 {
 3     vis[rt]=1;
 4   /*
 5     deal with ans
 6   */
 7     for(int i=adj[rt];i;i=s[i].next)
 8         if(!vis[s[i].zhong])
 9             totsize=size[s[i].zhong],root=0,
10             dfs1(s[i].zhong,0),solve(root);
11 }
咱們在每一次肯定的重心上進行計算,calc函數的內容隨題目而變,可是大概是長這樣的…… 本題的完整代碼:
 1 #include <cstdio>
 2 #include <cstring>
 3 #define N 20010
 4 #define inf 0x7fffffff
 5 #define max(a,b) ((a)>(b)?(a):(b))
 6 int n,e,adj[N],root,totsize,size[N],maxsize[N];
 7 struct edge{int zhong,val,next;}s[N<<1];
 8 inline void add(int qi,int zhong,int val)
 9     {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;s[e].val=val;}
10 bool mark[N];
11 inline void dfs1(int rt,int fa)
12 {
13     size[rt]=1,maxsize[rt]=0;
14     for(int i=adj[rt];i;i=s[i].next)
15         if(!mark[s[i].zhong]&&s[i].zhong!=fa)
16             dfs1(s[i].zhong,rt),size[rt]+=size[s[i].zhong],
17             maxsize[rt]=max(maxsize[rt],size[s[i].zhong]);
18     maxsize[rt]=max(maxsize[rt],totsize-size[rt]);
19     if(maxsize[rt]<maxsize[root])root=rt;
20 }
21 int cnt[3],dist[N],ans;
22 inline void dfs2(int rt,int fa)
23 {
24     ++cnt[dist[rt]];
25     for(int i=adj[rt];i;i=s[i].next)
26         if(!mark[s[i].zhong]&&s[i].zhong!=fa)
27             dist[s[i].zhong]=(dist[rt]+s[i].val)%3,
28             dfs2(s[i].zhong,rt);
29 }
30 inline int calc(int rt,int stval)
31 {
32     dist[rt]=stval,cnt[0]=cnt[1]=cnt[2]=0,dfs2(rt,0);
33     return cnt[0]*cnt[0]+2*cnt[1]*cnt[2];
34 }
35 inline void solve(int rt)
36 {
37     mark[rt]=1,ans+=calc(rt,0);
38     for(int i=adj[rt];i;i=s[i].next)
39         if(!mark[s[i].zhong])
40             ans-=calc(s[i].zhong,s[i].val),
41             totsize=size[s[i].zhong],root=0,
42             dfs1(s[i].zhong,0),solve(root);
43 }
44 inline int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
45 int main()
46 {
47     scanf("%d",&n);
48     register int i,a,b,c;
49     for(i=1;i<n;++i)
50         scanf("%d%d%d",&a,&b,&c),add(a,b,c%3),add(b,a,c%3);
51     totsize=n,root=0,maxsize[0]=inf;
52     dfs1(1,0),solve(root);
53     int di=n*n,d=gcd(di,ans);
54     printf("%d/%d\n",ans/d,di/d);
55 }
BZOJ2152
如今這個思路看起來比較蠢對吧……可是若是換一道題,本來的樹規作法就行不通了。

2599: [IOI2011]Race 

Time Limit: 70 Sec  Memory Limit: 128 MB
Description給一棵樹,每條邊有權.求一條簡單路徑,權值和等於K,且邊的數量最小.N <= 200000, K <= 1000000
Input第一行 兩個整數 n, k第二..n行 每行三個整數 表示一條無向邊的兩端和權值 (注意點的編號從0開始)
Output一個整數 表示最小邊數量 若是不存在這樣的路徑 輸出-1
Sample Input
4 3
0 1 1
1 2 2
1 3 4
Sample Output
2
看到這道題,本來樹規的作法顯然行不通了:
若是咱們定義$f[i][j]$爲以i爲根的子樹中長度爲j的路徑最小邊數,空間和時間複雜度都會爆炸。
這個時候咱們再想一想剛纔點分治的作法:
咱們僅用一個數組$f[j]$表示到每一個重心以後子樹中長度爲j的路徑最小邊數,
而後到每一個重心以後經過dfs遍歷去更新對應的f值,而後再用f數組之間相加==k的元素之和更新答案。
這樣,咱們就用一個$log$的代價,解決了樹規解決很困難的問題。時間複雜度$O(nlogn)$.
代碼: 
 1 #include <cstdio>
 2 #include <cstring>
 3 using namespace std;
 4 #define N 200010
 5 #define K 1000010
 6 #define LL long long
 7 #define max(a,b) ((a)>(b)?(a):(b))
 8 #define min(a,b) ((a)<(b)?(a):(b))
 9 int n,k,e,adj[N],ans,cnt[K];
10 bool vis[N];
11 int root,tot,size[N],maxs[N];
12 LL dis[N];
13 struct edge{int zhong,val,next;}s[N<<1];
14 inline void add(int qi,int zhong,int val)
15     {s[++e].zhong=zhong;s[e].val=val;s[e].next=adj[qi];adj[qi]=e;}
16 inline void dfs1(int rt,int fa)
17 {
18     size[rt]=1,maxs[rt]=0;
19     for(int i=adj[rt];i;i=s[i].next)
20         if(!vis[s[i].zhong]&&s[i].zhong!=fa)
21             dfs1(s[i].zhong,rt),size[rt]+=size[s[i].zhong],maxs[rt]=max(maxs[rt],size[s[i].zhong]);
22     maxs[rt]=max(maxs[rt],tot-size[rt]);
23     if(maxs[rt]<maxs[root])root=rt;
24 }
25 inline void dfs2(int rt,int fa,int deep)
26 {
27     if(dis[rt]>=0&&dis[rt]<=k)ans=min(ans,deep+cnt[k-dis[rt]]);
28     for(int i=adj[rt];i;i=s[i].next)
29         if(!vis[s[i].zhong]&&s[i].zhong!=fa)
30             dis[s[i].zhong]=dis[rt]+s[i].val,dfs2(s[i].zhong,rt,deep+1);
31 }
32 inline void update(int rt,int fa,int deep,bool opt)
33 {
34     if(dis[rt]>=0&&dis[rt]<=k)
35         if(opt)cnt[dis[rt]]=min(cnt[dis[rt]],deep);
36         else cnt[dis[rt]]=n;
37     for(int i=adj[rt];i;i=s[i].next)
38         if(!vis[s[i].zhong]&&s[i].zhong!=fa)
39             update(s[i].zhong,rt,deep+1,opt);
40 }
41 inline void solve(int rt)
42 {
43     vis[rt]=1;cnt[0]=0;
44     for(int i=adj[rt];i;i=s[i].next)
45         if(!vis[s[i].zhong])
46             dis[s[i].zhong]=s[i].val,dfs2(s[i].zhong,0,1),update(s[i].zhong,0,1,1);
47     for(int i=adj[rt];i;i=s[i].next)
48         if(!vis[s[i].zhong])
49             update(s[i].zhong,0,1,0);
50     for(int i=adj[rt];i;i=s[i].next)
51         if(!vis[s[i].zhong])
52             root=0,tot=size[s[i].zhong],dfs1(s[i].zhong,0),solve(root);
53 }
54 int main()
55 {
56     // freopen("Ark.in","r",stdin);
57     register int i,a,b,c;scanf("%d%d",&n,&k),ans=n;
58     for(i=1;i<n;++i)scanf("%d%d%d",&a,&b,&c),++a,++b,add(a,b,c),add(b,a,c);
59     for(i=1;i<=k;++i)cnt[i]=n;
60     root=0,maxs[0]=n,tot=n,dfs1(1,0),solve(root),
61     printf("%d\n",(ans==n)?-1:ans);
62 }
BZOJ2599

點分治最核心的思想就是這樣,找到重心,分別計算。其餘的習題還有:code

bzoj4016 (建最短路樹,再點分)

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <vector>
 4 #include <algorithm>
 5 using namespace std;
 6 #define N 30010
 7 #define inf 0x3fffffff
 8 #define max(a,b) ((a)>(b)?(a):(b))
 9 int n,m,k,e,adj[N],q[N<<4],hd,tl;bool vis[N];
10 struct edge{int to,next,val;}s[N<<1];
11 inline void add(int qi,int to,int val)
12     {s[++e].to=to;s[e].next=adj[qi];adj[qi]=e;s[e].val=val;}
13 int totsize,root,size[N],maxs[N],maxl[N],cnt[N],dis[N],ans=-inf,tot;
14 struct node{int to,val;};vector<node>to[N];
15 inline bool mt(const node &a,const node &b){return a.to<b.to;}
16 inline void dfs0(int rt)
17 {
18     vis[rt]=1;
19     for(int i=0,l=to[rt].size();i<l;++i)
20         if(!vis[to[rt][i].to]&&dis[to[rt][i].to]==dis[rt]+to[rt][i].val)
21             add(rt,to[rt][i].to,to[rt][i].val),add(to[rt][i].to,rt,to[rt][i].val),dfs0(to[rt][i].to);
22 }
23 inline void spfa_and_build()
24 {
25     memset(dis,0x3f,sizeof(dis));
26     q[hd=tl=1]=1,vis[1]=1,dis[1]=0;
27     register int i,x,l;
28     while(hd<=tl)
29         for(x=q[hd++],vis[x]=0,i=0,l=to[x].size();i<l;++i)
30             if(dis[to[x][i].to]>dis[x]+to[x][i].val)
31             {
32                 dis[to[x][i].to]=dis[x]+to[x][i].val;
33                 if(!vis[to[x][i].to])vis[to[x][i].to]=1,q[++tl]=to[x][i].to;
34             }
35     dfs0(1);
36 }
37 inline void dfs1(int rt,int fa)
38 {
39     size[rt]=1,maxs[rt]=0;
40     for(int i=adj[rt];i;i=s[i].next)
41         if(!vis[s[i].to]&&s[i].to!=fa)
42             dfs1(s[i].to,rt),size[rt]+=size[s[i].to],maxs[rt]=max(maxs[rt],size[s[i].to]);
43     maxs[rt]=max(maxs[rt],totsize-size[rt]);
44     if(maxs[rt]<maxs[root])root=rt;
45 }
46 inline void dfs2(int rt,int fa,int dp)
47 {
48     if(maxl[k-dp-1]!=-inf)
49         if(ans<dis[rt]+maxl[k-dp-1])ans=dis[rt]+maxl[k-dp-1],tot=cnt[k-dp-1];
50         else if(ans==dis[rt]+maxl[k-dp-1])tot+=cnt[k-dp-1];
51     if(dp+1<k)for(int i=adj[rt];i;i=s[i].next)
52         if(!vis[s[i].to]&&s[i].to!=fa)
53             dis[s[i].to]=dis[rt]+s[i].val,dfs2(s[i].to,rt,dp+1);
54 }
55 inline void update(int rt,int fa,int dp)
56 {
57     if(maxl[dp]<dis[rt])maxl[dp]=dis[rt],cnt[dp]=1;
58     else if(maxl[dp]==dis[rt])++cnt[dp];
59     if(dp+1<k)for(int i=adj[rt];i;i=s[i].next)
60         if(!vis[s[i].to]&&s[i].to!=fa)
61             update(s[i].to,rt,dp+1);
62 }
63 inline void solve(int rt)
64 {
65     vis[rt]=1,maxl[0]=0,cnt[0]=1;
66     for(int i=adj[rt];i;i=s[i].next)
67         if(!vis[s[i].to])dis[s[i].to]=s[i].val,dfs2(s[i].to,0,1),update(s[i].to,0,1);
68     for(int i=1;i<=k;++i)maxl[i]=-inf,cnt[i]=0;
69     // for(int i=adj[rt];i;i=s[i].next)if(!vis[s[i].to])update(s[i].to,0,1,0);
70     for(int i=adj[rt];i;i=s[i].next)
71         if(!vis[s[i].to]&&size[s[i].to]>=k)root=0,totsize=size[s[i].to],dfs1(s[i].to,0),solve(root);
72 }
73 inline void work_and_print()
74 {
75     memset(vis,0,sizeof(vis)),memset(dis,0,sizeof(dis)),memset(cnt,0,sizeof(cnt));
76     for(int i=1;i<=k;++i)maxl[i]=-inf;
77     totsize=n,root=0,maxs[0]=inf,dfs1(1,0),solve(root);
78     printf("%d %d",ans,tot);
79 }
80 int main()
81 {
82     scanf("%d%d%d",&n,&m,&k);
83     register int i,j,l,a,b,c;
84     for(i=1;i<=m;++i)
85         scanf("%d%d%d",&a,&b,&c),to[a].push_back((node){b,c}),to[b].push_back((node){a,c});
86     for(i=1;i<=n;++i)sort(to[i].begin(),to[i].end(),mt);
87     spfa_and_build(),work_and_print();
88 }
BZOJ4016

bzoj3672 (推式子,點分治+CDQ維護凸殼)

bzoj1758 (01分數規劃,卡精卡時……)

二.從點分治到動態點分治

作着作着題,你可能會發現有的題目要修改……這可怎麼辦呢。
咱們確定不能一次修改跑一次DP或者點分治吧……
那麼咱們考慮用數組或者數據結構來維護咱們須要的信息,每一次修改都維護數據結構中對應的信息。
這其實借用了樹規中的思想,咱們經過維護這些信息,能夠實現點之間$O(1)$或者$O(logn)$的轉移。
但是若是咱們對原樹中的點維護這些數據結構,一條鏈就能夠送咱們上天……
但同時咱們觀察到,若是咱們把點分治時每一層的重心之間連邊,這就構成了一顆高度爲$logn$的新樹,咱們叫它分治樹。
那麼咱們就用數據結構維護分治樹種對應咱們須要的信息。
一個經常使用的思想:維護當前子樹中到父親的某些信息,從而減少查詢的複雜度……
這樣說可能有些抽象……咱們來看道題:

1095: [ZJOI2007]Hide 捉迷藏

Time Limit: 40 Sec  Memory Limit: 256 MB

Description

  捉迷藏 Jiajia和Wind是一對恩愛的夫妻,而且他們有不少孩子。某天,Jiajia、Wind和孩子們決定在家裏玩
捉迷藏遊戲。他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,這N-1條走廊的分佈使得任意兩個屋
子都互相可達。遊戲是這樣進行的,孩子們負責躲藏,Jiajia負責找,而Wind負責操縱這N個屋子的燈。在起初的
時候,全部的燈都沒有被打開。每一次,孩子們只會躲藏在沒有開燈的房間中,可是爲了增長刺激性,孩子們會要
求打開某個房間的電燈或者關閉某個房間的電燈。爲了評估某一次遊戲的複雜性,Jiajia但願知道可能的最遠的兩
個孩子的距離(即最遠的兩個關燈房間的距離)。 咱們將以以下形式定義每一種操做: C(hange) i 改變第i個房
間的照明狀態,若原來打開,則關閉;若原來關閉,則打開。 G(ame) 開始一次遊戲,查詢最遠的兩個關燈房間的
距離。

Input

  第一行包含一個整數N,表示房間的個數,房間將被編號爲1,2,3…N的整數。接下來N-1行每行兩個整數a, b,
表示房間a與房間b之間有一條走廊相連。接下來一行包含一個整數Q,表示操做次數。接着Q行,每行一個操做,如
上文所示。

Output

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

Sample Input

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

Sample Output

4
3
3
4

HINT

對於100%的數據, N ≤100000, M ≤500000。

 

 那麼咱們看這個題,若是這個題不用修改的話,$O(n)$的樹規和$O(nlogn)$的點分治均可以解決。

想一下,咱們須要什麼變量呢?須要最長鏈和次長鏈拼起來對吧……

那麼咱們能夠用一些數據結構維護每一個點在分治樹中伸下去與子樹中的每一個黑點之間鏈的長度,

這個數據結構咱們能夠用大根堆,那麼最長鏈就是堆頂了。

但同時,咱們意識到最長鏈和次長鏈不能來自同一棵分治樹下的子樹,

所以咱們再來一個數據結構,維護這個點每一個分治樹兒子的堆頂(這就是分治樹中「維護父親/維護孩子」思想的體現,雖然不是很明顯),咱們還能夠用個堆。

那麼這個堆的前兩個元素之和就是通過這個點的最長鏈長度,咱們對於每一個點均可以維護這樣一個變量(固然,也可能不存在)。

那麼咱們再用一個大根堆維護每一個節點的答案,那麼這個堆的堆頂就是答案了。

每次修改的時候,咱們在第一類堆中刪除或者添加對應的鏈長,維護第二類堆對應子樹的元素,再更新第三類堆中的答案,就實現了一次維護。

分治樹的深度是$logn$的,堆操做的是$logn$的,所以時間複雜度爲$O(nlog^{2}n)$。

代碼實現:

  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 }
BZOJ1095

其實這道題是比較簡單的……那麼咱們再看一道題。

3924: [Zjoi2015]幻想鄉戰略遊戲

Time Limit: 100 Sec  Memory Limit: 256 MB

Description

 傲嬌少女幽香正在玩一個很是有趣的戰略類遊戲,原本這個遊戲的地圖其實還不算太大,幽香還能管得過來,可是不知道爲何如今的網遊廠商把遊戲的地圖越作越大,以致於幽香一眼根本看不過來,更別說和別人打仗了。 在打仗以前,幽香如今面臨一個很是基本的管理問題須要解決。 整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊鏈接起來,使得每兩個點之間有一條惟一的路徑將它們鏈接起來。在遊戲中,幽香可能在空地上增長或者減小一些軍隊。同時,幽香能夠在一個空地上放置一個補給站。 若是補給站在點u上,而且空地v上有dv個單位的軍隊,那麼幽香天天就要花費dv×dist(u,v)的金錢來補給這些軍隊。因爲幽香須要補給全部的軍隊,所以幽香總共就要花費爲Sigma(Dv*dist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(惟一路徑的權和)。 由於遊戲的規定,幽香只能選擇一個空地做爲補給站。在遊戲的過程當中,幽香可能會在某些空地上製造一些軍隊,也可能會減小某些空地上的軍隊,進行了這樣的操做之後,出於經濟上的考慮,幽香每每能夠移動他的補給站從而省一些錢。可是因爲這個遊戲的地圖是在太大了,幽香沒法輕易的進行最優的安排,你能幫幫她嗎? 你能夠假定一開始全部空地上都沒有軍隊。

PDF版試題:JudgeOnline/upload/201708/zjoi2015d1.pdf

Input

第一行兩個數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<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5
對於全部數據,這個樹上全部點的度數都不超過20
N,Q>=1
 

Output

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

Sample Input

10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 61
2 7 1
5 8 1
7 91
1 10 1
3 1
2 1
8 1
3 1
4 1

Sample Output

0
1
4
5
6
 
怎麼操做呢?咱們不難發現,因爲目標函數是$Σd_{v}*dis(u,v)$,由定義可知,咱們的目標其實就是求帶權的重心
那麼怎麼求呢……仍是先假設咱們有一棵靜態的樹,那麼考慮樹規。
想一下樹規中咱們都求了什麼:
設$s1[i]$爲i子樹點權和,$s2[i]$爲i子樹點到它的距離和,$s3[i]$爲i父親到它的距離和。
那麼轉移應該是
$ans[i]=ans[fa]+s2[fa]-s2[i]-s1[i]*dis(i,fa)+s3[fa]+(sum-s1[i])*dis(i,fa)$
dis咱們能夠用ST表RMQ或者倍增LCA來求.
但其實咱們是能夠不要s3數組,一層一層跳着往上求$s2[fa]-s2[i]-s1[i]*dis(i,fa)$這個式子的。
因爲原樹多是條鏈,咱們不敢直接跳,只好維護s3數組。
可是若是修改的話,s3涉及到的修改會特別多!
咱們聯想到,分治樹的高度是$logn$的!
所以咱們能夠不維護s3,僅僅維護s1和s2,在分治樹上用$O(logn)$的時間求出某一個點做爲帶權重心時的權和。
可是你會發現,咱們上面的式子裏有一個-s2[i]很難處理:在樹規中i和fa只隔了一條邊,可是在分治樹中,你並不知道元素長什麼樣子!
所以咱們仍是得加一個s3數組,s3[i]表示i的分治樹子節點到i分治樹父親的權和。這就是咱們動態樹分治中「維護父親」思想的典型體現
那麼咱們如今就能夠以$O(logn)$的複雜度枚舉i的祖先fa,求$Σs2[fa]-s3[i]+(s1[fa]-s1[i])*dis(i,fa)$來求與i在原樹上的lca是fa的點到i的距離了。
*這種$O(logn)$跳父親的作法也是一種常見的思想。
而後對於統計答案,咱們能夠每次從分治樹的根出發,看往他的哪一個兒子走答案更優。
咱們最多走$logn$層,每一層最多進行20次$O(logn)$的計算,所以查詢是$O(20log^{2}n)$的.
總的複雜度$O(20Qlog^{2}n)$。代碼見下:
  1 #include <cstdio>
  2 #include <cstring>
  3 using namespace std;
  4 #define LL long long
  5 #define N 100010
  6 #define LL long long
  7 #define max(a,b) ((a)>(b)?(a):(b))
  8 #define min(a,b) ((a)<(b)?(a):(b))
  9 struct edge{int zhong,next,val;};
 10 struct G
 11 {
 12     edge s[N<<1];int e,adj[N];
 13     G(){e=0;memset(adj,0,sizeof(adj));}
 14     inline void add(int qi,int zhong,int val=0)
 15         {s[++e].zhong=zhong;s[e].next=adj[qi];adj[qi]=e;s[e].val=val;}
 16 }T1,T2;
 17 int n,q,f[N<<1][18],logn[N<<1],bin[25],tp;
 18 LL s[3][N],sum,ans,len[N];
 19 int dfn[N],num;
 20 inline void dfs1(int rt,int fa)
 21 {
 22     f[(dfn[rt]=++num)][0]=len[rt];
 23     for(int i=T1.adj[rt];i;i=T1.s[i].next)
 24         if(T1.s[i].zhong!=fa)
 25             len[T1.s[i].zhong]=len[rt]+T1.s[i].val,dfs1(T1.s[i].zhong,rt),f[++num][0]=len[rt];
 26 }
 27 inline LL LCA(int a,int b)
 28 {
 29     if(dfn[a]>dfn[b])a^=b,b^=a,a^=b;
 30     int k=logn[dfn[b]-dfn[a]+1];
 31     return min(f[dfn[a]][k],f[dfn[b]-bin[k]+1][k])<<1;
 32 }
 33 inline LL dis(int a,int b){return len[a]+len[b]-LCA(a,b);}
 34 int size[N],maxs[N],totsize,root,Vater[N];
 35 bool vis[N];
 36 inline void dfs2(int rt,int fa)
 37 {
 38     size[rt]=1,maxs[rt]=0;
 39     for(int i=T1.adj[rt];i;i=T1.s[i].next)
 40         if(!vis[T1.s[i].zhong]&&T1.s[i].zhong!=fa)
 41             dfs2(T1.s[i].zhong,rt),size[rt]+=size[T1.s[i].zhong],
 42             maxs[rt]=max(size[T1.s[i].zhong],maxs[rt]);
 43     maxs[rt]=max(totsize-size[rt],maxs[rt]);
 44     if(maxs[rt]<maxs[root])root=rt;
 45 }
 46 inline void dfs3(int rt,int fa)
 47 {
 48     Vater[rt]=fa;vis[rt]=1;int siz=totsize;
 49     for(int i=T1.adj[rt];i;i=T1.s[i].next)
 50         if(!vis[T1.s[i].zhong])
 51         {
 52             if(size[T1.s[i].zhong]>size[rt])totsize=siz-size[rt];
 53             else totsize=size[T1.s[i].zhong];
 54             root=0,dfs2(T1.s[i].zhong,0),T2.add(rt,root,T1.s[i].zhong),dfs3(root,rt);
 55         }
 56 }
 57 inline void update(int who,LL val)
 58 {
 59     for(int rt=who;rt;rt=Vater[rt])
 60     {
 61         s[0][rt]+=val,s[1][rt]+=dis(rt,who)*val;
 62         if(Vater[rt])s[2][rt]+=dis(Vater[rt],who)*val;
 63     }
 64 }
 65 inline LL calc(int rt)
 66 {
 67     LL ret=0;
 68     for(int x=rt;x;x=Vater[x])
 69     {
 70         ret+=s[1][x];
 71         if(Vater[x])ret+=-s[2][x]+(s[0][Vater[x]]-s[0][x])*dis(Vater[x],rt);
 72     }
 73     return ret;
 74 }
 75 inline LL getans(int rt)
 76 {
 77     LL temp=calc(rt);
 78     for(int i=T2.adj[rt];i;i=T2.s[i].next)
 79         if(calc(T2.s[i].val)<temp)return getans(T2.s[i].zhong);//*******
 80     return temp;
 81 }
 82 char B[1<<15],*S=B,*T=B;
 83 #define getc ( S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
 84 inline int read()
 85 {
 86     int x=0,f=1;register char c=getc;
 87     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getc;}
 88     while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
 89     return x*f;
 90 }
 91 int main()
 92 {
 93     register int i,j,orig,a,b,c;
 94     n=read();q=read();
 95     for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1;
 96     while(bin[tp+1]<=(n<<1))++tp;
 97     for(logn[0]=-1,i=1;i<=(n<<1);++i)logn[i]=logn[i>>1]+1;
 98     for(i=1;i<n;++i)
 99         a=read(),b=read(),c=read(),T1.add(a,b,c),T1.add(b,a,c);
100     dfs1(1,0),root=0,maxs[0]=n+1,totsize=n,dfs2(1,0);
101     for(i=1;i<=tp;++i)
102         for(j=1;j+bin[i]-1<=(n<<1);++j)
103             f[j][i]=min(f[j][i-1],f[j+bin[i-1]][i-1]);
104     orig=root,dfs3(root,0);
105     while(q--)
106         a=read(),b=read(),update(a,b),printf("%lld\n",getans(orig));
107 }
BZOJ4012

 咱們再來一道大同小異的題目:

4012: [HNOI2015]開店

Time Limit: 70 Sec  Memory Limit: 512 MB

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

 

如今你應該能想到怎麼作了吧!

咱們用數據結構維護以i爲分治樹根的子樹每一個年齡的妖怪到i的距離,以及到i分治樹父親的距離,而後用數據結構求和。

數據結構用什麼呢?線段樹?$O(nlog^{2}n)$的空間複雜度確定會MLE的。

注意到這個求和能夠寫爲前綴和相減的形式,所以咱們用能夠用$O(nlogn)$的空間複雜度的vector+sort排序節省內存。

那麼這道題就被解決啦……代碼見下:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <vector>
  4 #include <algorithm>
  5 using namespace std;
  6 #define N 150010
  7 #define LL long long
  8 int n,e,tot,adj[N],q,maxn,val[N];
  9 char B[1<<15],*S=B,*T=B;
 10 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
 11 inline int read()
 12 {
 13     int x=0;register char c=getc;
 14     while(c<'0'||c>'9')c=getc;
 15     while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
 16     return x;
 17 }
 18 #define min(a,b) ((a)<(b)?(a):(b))
 19 #define max(a,b) ((a)>(b)?(a):(b))
 20 struct edge{int zhong,next,val;}s[N<<1];
 21 inline void add(int qi,int zhong,int val)
 22     {s[++e].zhong=zhong,s[e].val=val,s[e].next=adj[qi],adj[qi]=e;}
 23 int f[N<<1][19],deep[N],dfn[N],num,bin[25],tp,logn[N<<1];
 24 inline void ST()
 25 {
 26     for(int i=1;i<=tp;++i)
 27         for(int j=1;j+bin[i]-1<=(n<<1);++j)
 28             f[j][i]=min(f[j][i-1],f[j+bin[i-1]][i-1]);
 29 }
 30 inline void dfs1(int rt,int fa,int len)
 31 {
 32     f[(dfn[rt]=++num)][0]=deep[rt]=len;
 33     for(int i=adj[rt];i;i=s[i].next)
 34         if(s[i].zhong!=fa)
 35             dfs1(s[i].zhong,rt,len+s[i].val),f[++num][0]=len;
 36 }
 37 int Vater[N],size[N],maxs[N],totsize,root;
 38 bool vis[N];
 39 inline void dfs2(int rt,int fa)
 40 {
 41     size[rt]=1,maxs[rt]=0;
 42     for(int i=adj[rt];i;i=s[i].next)
 43         if(!vis[s[i].zhong]&&s[i].zhong!=fa)
 44             dfs2(s[i].zhong,rt),size[rt]+=size[s[i].zhong],
 45             maxs[rt]=max(maxs[rt],size[s[i].zhong]);
 46     maxs[rt]=max(maxs[rt],totsize-size[rt]);
 47     if(maxs[rt]<maxs[root])root=rt;
 48 }
 49 inline LL dis(int a,int b)
 50 {
 51     if(dfn[a]>dfn[b])a^=b,b^=a,a^=b;
 52     int k=logn[dfn[b]-dfn[a]+1];
 53     return deep[a]+deep[b]-(min(f[dfn[a]][k],f[dfn[b]-bin[k]+1][k])<<1);
 54 }
 55 struct node
 56 {
 57     int val;LL size[3];
 58     node(int a=0,LL b=0,LL c=0,LL d=0){val=a,size[0]=b,size[1]=c,size[2]=d;}
 59     inline bool operator < (const node &b)const
 60         {return val<b.val;}
 61 };
 62 vector<node>sta[N];
 63 inline void dfs3(int rt,int fa,int Vatti)
 64 {
 65     sta[Vatti].push_back(node(val[rt],1,dis(rt,Vatti), Vater[Vatti]?dis(rt,Vater[Vatti]):0 ));
 66     for(int i=adj[rt];i;i=s[i].next)
 67         if(s[i].zhong!=fa&&!vis[s[i].zhong])
 68             dfs3(s[i].zhong,rt,Vatti);
 69 }
 70 inline void dfs4(int rt,int fa)
 71 {
 72     Vater[rt]=fa,vis[rt]=1;
 73     int siz=totsize;
 74     dfs3(rt,0,rt);sta[rt].push_back(node(-1,0,0,0));
 75     sort(sta[rt].begin(),sta[rt].end());
 76     for(int i=0,j=sta[rt].size();i<j-1;++i)
 77         sta[rt][i+1].size[0]+=sta[rt][i].size[0],
 78         sta[rt][i+1].size[1]+=sta[rt][i].size[1],
 79         sta[rt][i+1].size[2]+=sta[rt][i].size[2];
 80     for(int i=adj[rt];i;i=s[i].next)
 81         if(!vis[s[i].zhong])
 82         {
 83             if(size[s[i].zhong]>size[rt])totsize=siz-size[rt];
 84             else totsize=size[s[i].zhong];
 85             root=0,dfs2(s[i].zhong,0),dfs4(root,rt);
 86         }
 87 }
 88 inline node query(int id,int l,int r)
 89 {
 90     if(id==0)return node();
 91     vector<node>::iterator it1=upper_bound(sta[id].begin(),sta[id].end(),node(r,0,0,0) );--it1;
 92     vector<node>::iterator it2=upper_bound(sta[id].begin(),sta[id].end(),node(l-1,0,0,0));--it2;
 93     return node(0,it1->size[0]-it2->size[0],it1->size[1]-it2->size[1],it1->size[2]-it2->size[2]);
 94 }
 95 inline LL calc(int rt,int l,int r)
 96 {
 97     LL ret=0;
 98     // printf("rt=%d l=%d r=%d\n",rt,l,r);
 99     for(int x=rt;x;x=Vater[x])
100     {
101         node a=query(x,l,r);
102         ret+=a.size[1];
103         if(x!=rt)ret+=a.size[0]*dis(x,rt);
104         if(Vater[x])ret-=a.size[2]+a.size[0]*dis(Vater[x],rt);
105     }
106     return ret;
107 }
108 int main()
109 {
110     register int i,j,a,b,c;LL ans=0;
111     n=read(),q=read(),maxn=read();
112     for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1;
113     while(bin[tp+1]<=(n<<1))++tp;
114     for(logn[0]=-1,i=1;i<=(n<<1);++i)logn[i]=logn[i>>1]+1;
115     for(i=1;i<=n;++i)val[i]=read();
116     for(i=1;i<n;++i)
117         a=read(),b=read(),c=read(),add(a,b,c),add(b,a,c);
118     dfs1(1,0,0),ST();
119     root=0,maxs[0]=n+1,totsize=n,dfs2(1,0);
120     dfs4(root,0);
121     while(q--)
122     {
123         a=read(),b=read(),c=read();
124         b=(b+ans)%maxn,c=(c+ans)%maxn;
125         if(b>c)b^=c,c^=b,b^=c;
126         printf("%lld\n",ans=calc(a,b,c));
127     }
128 }
BZOJ4012

最後咱們來個大boss試試知難♂而上

 

3435: [Wc2014]紫荊花之戀

Time Limit: 240 Sec  Memory Limit: 512 MB

Description

強強和萌萌是一對好朋友。有一天他們在外面閒逛,忽然看到前方有一棵紫荊樹。這已是紫荊花飛舞的季節了,無數的花瓣以肉眼可見的速度從紫荊樹上長了出來。仔細看看的話,這個大樹其實是一個帶權樹。每一個時刻它會長出一個新的葉子節點。每一個節點上有一個可愛的小精靈,新長出的節點上也會同時出現一個新的小精靈。小精靈是很萌可是也很脆弱的生物,每一個小精靈 i 都有一個感覺能力值Ri ,小精靈 i, j 成爲朋友當且僅當在樹上 i 和 j 的距離 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在這個樹上從 i 到 j 的惟一路徑上全部邊的邊權和。強強和萌萌很好奇每次新長出一個葉子節點以後,這個樹上總共有幾對朋友。  
咱們假定這個樹一開始爲空,節點按照加入的順序從 1開始編號。因爲強強很是好奇, 你必須在他每次出現新節點後立刻給出總共的朋友對數,不能拖延哦。 

Input

共有 n + 2 行。 
第一行包含一個正整數,表示測試點編號。 
第二行包含一個正整數 n ,表示總共要加入的節點數。 
咱們令加入節點前的總共朋友對數是 last_ans,在一開始時它的值爲0。 
接下來 n 行中第 i 行有三個數 ai, bi, ri,表示節點  i  的父節點的編號爲 ai xor (last_ans mod 10^9)   (其中xor 表示異或,mod  表示取餘,數據保證這樣操做後獲得的結果介於 1到i  –  1之間),與父節點之間的邊權爲 ci,節點 i 上小精靈的感覺能力值爲r!。 
注意 a1 = c1 = 0,表示 1 號點是根節點,對於 i > 1,父節點的編號至少爲1。

Output

包含 n 行,每行輸出1 個整數, 表示加入第 i 個點以後,樹上有幾對朋友。

Sample Input

0
5
0 0 6
1 2 4
0 9 4
0 5 5
0 2 4 

Sample Output

0
1
2
4

HINT

1<=Ci<=10000
Ai<=2*10^9
Ri<=10^9
N<=100000

 

那麼這題怎麼作呢……

咱們先考慮把題設式子變形:

\[dis(u,v)<=r_{u}+r_{v}  \]

\[dis(u,lca)+dis(lca,v)<=r_{u}+r_{v}  \]

\[r_{u}-dis(lca,u)>=dis(lca,v)-r_{v}\]

那麼接下來咱們沿用上面幾題的思想,在原樹上每一個節點開2棵平衡樹,維護以i爲根的子樹中的$dis(i,u)-r_{u}$值和$dis(fa[i],u)-r_{u}$值。

這樣每次咱們重新插入的節點開始往上爬到根,而且更新答案便可。

可是咱們發現,若是這是條鏈……時間複雜度會被卡到$O(n^{2})$

可是咱們轉念一想,點分樹是相對平衡的,深度是$logn$級別的呀!

因此咱們借用替罪羊拍扁重建的思想,若是在插入結束後,

某個節點的某個兒子特別大(用$alpha$判斷),咱們就把那棵子樹重構爲一棵分治樹。

這樣咱們的複雜度能夠有$O(nlog^{2}n)$的保障。

在代碼實現的時候,要特別注意重構時和原來這個子樹的分治樹父親之間信息的維護。

代碼:

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <vector>
  5 using namespace std;
  6 #define LL long long
  7 #define inf 1000000000
  8 #define N 100010
  9 #define alpha 0.755
 10 int n,e,adj[N],val[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 vector<int> to[N];
 15 int f[N][18],bin[25],tp,deep[N],len[N];
 16 inline int LCA(int a,int b)
 17 {
 18     if(deep[a]<deep[b])a^=b,b^=a,a^=b;
 19     int i,cha=deep[a]-deep[b];
 20     for(i=tp;~i;--i)if(cha&bin[i])a=f[a][i];
 21     if(a==b)return a;
 22     for(i=tp;~i;--i)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
 23     return f[a][0];
 24 }
 25 inline int dis(int a,int b){return len[a]+len[b]-len[LCA(a,b)]*2;}
 26 struct Goat
 27 {
 28     int val,size;Goat *ch[2];
 29     Goat(){}
 30     inline bool bad()
 31         {return ch[0]->size>=size*alpha+5 || ch[1]->size>=size*alpha+5; }
 32 }*tree1[N],*tree2[N],mem[N<<8],*pool[N<<8],*null,*sta[N];
 33 int tot,top;
 34 inline void intn()
 35 {
 36     null=new Goat();
 37     null->ch[0]=null->ch[1]=null,null->val=null->size=0;
 38     for(int i=0;i<(N<<8);++i)pool[i]=mem+i;
 39     tot=(N<<8)-1;
 40     for(int i=0;i<=n;++i)tree1[i]=tree2[i]=null;
 41 }
 42 inline Goat** insert(Goat *&a,int val)
 43 {
 44     if(a==null)
 45     {
 46         a=pool[tot--],a->ch[0]=a->ch[1]=null;
 47         a->val=val,a->size=1;return &null;
 48     }
 49     ++a->size;
 50     Goat **o=insert(a->ch[a->val<val],val);
 51     if(a->bad())o=&a;return o;
 52 }
 53 inline int get_rank(Goat *o,int val)
 54 {
 55     if(o==null)return 0;
 56     return (o->val>=val)?get_rank(o->ch[0],val):(get_rank(o->ch[1],val)+o->ch[0]->size+1);
 57 }
 58 inline void Erholung(Goat *o)
 59 {
 60     if(o==null)return;
 61     if(o->ch[0]!=null)Erholung(o->ch[0]);
 62     pool[++tot]=o;
 63     if(o->ch[1]!=null)Erholung(o->ch[1]);
 64 }
 65 inline void travel(Goat *o)
 66 {
 67     if(o==null)return;
 68     if(o->ch[0]!=null)travel(o->ch[0]);
 69     sta[++top]=o;
 70     if(o->ch[1]!=null)travel(o->ch[1]);
 71 }
 72 inline Goat* build(int l,int r)
 73 {
 74     if(l>r)return null;
 75     int mi=l+r>>1;
 76     Goat *o=sta[mi];o->size=r-l+1;
 77     o->ch[0]=build(l,mi-1),o->ch[1]=build(mi+1,r);
 78     return o;
 79 }
 80 inline void rebuild(Goat *&o){top=0,travel(o),o=build(1,top);}
 81 inline void Insert(Goat *&a,int val)
 82 {
 83     Goat **o=insert(a,val);
 84     if(*o!=null)rebuild(*o);
 85 }
 86 int size[N],maxs[N],totsize,root,Vater[N];
 87 #define max(a,b) ((a)>(b)?(a):(b))
 88 #define min(a,b) ((a)<(b)?(a):(b))
 89 bool vis[N];
 90 inline void dfs1(int rt,int fa)
 91 {
 92     size[rt]=1,maxs[rt]=0;
 93     for(int i=adj[rt];i;i=s[i].next)
 94         if(!vis[s[i].zhong]&&s[i].zhong!=fa)
 95             dfs1(s[i].zhong,rt),size[rt]+=size[s[i].zhong],
 96             maxs[rt]=max(maxs[rt],size[s[i].zhong]);
 97     maxs[rt]=max(maxs[rt],totsize-size[rt]);
 98     if(maxs[rt]<maxs[root])root=rt;
 99 }
100 inline void dfs2(int rt,int fa,int Vatti)
101 {
102     Insert(tree1[Vatti],dis(rt,Vatti)-val[rt]);
103     if(Vater[Vatti])Insert(tree2[Vatti],dis(rt,Vater[Vatti])-val[rt]);
104     for(int i=adj[rt];i;i=s[i].next)
105         if(s[i].zhong!=fa&&!vis[s[i].zhong])
106             dfs2(s[i].zhong,rt,Vatti);
107 }
108 inline void dfs3(int rt,int fa)
109 {
110     Vater[rt]=fa,vis[rt]=1;
111     int siz=totsize;
112     dfs2(rt,0,rt);
113     for(int i=adj[rt];i;i=s[i].next)
114         if(!vis[s[i].zhong])
115         {
116             if(size[s[i].zhong]>size[rt])totsize=siz-size[rt];
117             else totsize=size[s[i].zhong];
118             root=0,dfs1(s[i].zhong,rt),
119             to[rt].push_back(root),dfs3(root,rt);
120         }
121 }
122 inline void recover(int x)
123 {
124     ++totsize,vis[x]=0,
125     Erholung(tree1[x]),Erholung(tree2[x]),
126     tree1[x]=tree2[x]=null;
127     for(int i=0,k=to[x].size();i<k;++i)recover(to[x][i]);
128     to[x].clear();
129 }
130 inline void rebuild(int x)
131 {
132     totsize=0,recover(x),root=0,dfs1(x,0);
133     if(Vater[x])for(int i=0,j=to[Vater[x]].size();i<j;++i)
134         if(to[Vater[x]][i]==x)to[Vater[x]][i]=root;
135     dfs3(root,Vater[x]);
136 }
137 LL ans=0;
138 inline int insert(int x)
139 {
140     int rt,ds,ret=0;
141     for(rt=x;Vater[rt];rt=Vater[rt])
142         ds=val[x]-dis(x,Vater[rt])+1,ans+=get_rank(tree1[Vater[rt]],ds)-get_rank(tree2[rt],ds);
143     for(rt=x;rt;rt=Vater[rt])
144     {
145         Insert(tree1[rt],dis(x,rt)-val[x]);
146         if(Vater[rt])Insert(tree2[rt],dis(x,Vater[rt])-val[x]);
147     }
148     for(rt=x;Vater[rt];rt=Vater[rt])
149         if(tree1[rt]->size>=tree1[Vater[rt]]->size*alpha+5)ret=Vater[rt];//*****
150     return ret;
151 }b 
152 char B[1<<15],*S=B,*T=B;
153 #define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
154 inline int read()
155 {
156     int x=0;register char c=getc;
157     while(c<'0'||c>'9')c=getc;
158     while(c>='0'&&c<='9')x=10*x+(c^48),c=getc;
159     return x;
160 }
161 int main()
162 {
163     n=read(),n=read();
164     register int i,j,x,b;
165     for(bin[0]=i=1;i<=20;++i)bin[i]=bin[i-1]<<1;
166     while(bin[tp+1]<=n)++tp;
167     maxs[0]=inf,root=0,intn();
168     for(i=1;i<=n;++i)
169     {
170         Vater[i]=f[i][0]=read()^(ans%inf),b=read(),val[i]=read();
171         deep[i]=deep[f[i][0]]+1,len[i]=len[f[i][0]]+b;vis[i]=1;
172         if(Vater[i])to[Vater[i]].push_back(i),add(f[i][0],i),add(i,f[i][0]);
173         for(int j=1;bin[j]+1<=deep[i];++j)f[i][j]=f[f[i][j-1]][j-1];
174         x=insert(i);if(x)rebuild(x);
175         printf("%lld\n",ans);
176     }
177 }
BZOJ3435

三.總結

點分治和動態樹分治實際上是對樹規思想的變形。咱們利用分治樹高度很小的優勢,對每一個點維護相應的信息,

經過$O(logn)$的額外複雜度來進行普通樹規沒法完成的操做。但願本文能對你有幫助!

相關文章
相關標籤/搜索