(寫篇博客證實本身還活着×2)html
轉載請註明原文地址:http://www.cnblogs.com/LadyLex/p/8006488.html node
有的時候,咱們會發現這樣一類題:它長得很像一個$O(n)$的樹規,數組
可是卻很難用單獨的數組維護對應的信息,這樣咱們就有了澱粉質點分治。數據結構
經過直接統計($O(nlogn)$)或者加上數據結構(好比樹狀數組,堆,線段樹等等)維護信息($O(nlog^{2}n)$),ide
咱們能夠統計以前很差統計的東西函數
這篇博客將會介紹點分治以及動態點分治的簡單應用,但願閱讀本文的你能有所收穫,那就再好不過了。測試
一.樹規到點分治ui
咱們先來看一道簡單的題目……spa
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 }
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 }
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 }
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 }
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 }
點分治最核心的思想就是這樣,找到重心,分別計算。其餘的習題還有: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 }
bzoj3672 (推式子,點分治+CDQ維護凸殼)
bzoj1758 (01分數規劃,卡精卡時……)
二.從點分治到動態點分治
作着作着題,你可能會發現有的題目要修改……這可怎麼辦呢。 捉迷藏 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。
對於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 }
其實這道題是比較簡單的……那麼咱們再看一道題。
傲嬌少女幽香正在玩一個很是有趣的戰略類遊戲,原本這個遊戲的地圖其實還不算太大,幽香還能管得過來,可是不知道爲何如今的網遊廠商把遊戲的地圖越作越大,以致於幽香一眼根本看不過來,更別說和別人打仗了。 在打仗以前,幽香如今面臨一個很是基本的管理問題須要解決。 整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊鏈接起來,使得每兩個點之間有一條惟一的路徑將它們鏈接起來。在遊戲中,幽香可能在空地上增長或者減小一些軍隊。同時,幽香能夠在一個空地上放置一個補給站。 若是補給站在點u上,而且空地v上有dv個單位的軍隊,那麼幽香天天就要花費dv×dist(u,v)的金錢來補給這些軍隊。因爲幽香須要補給全部的軍隊,所以幽香總共就要花費爲Sigma(Dv*dist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(惟一路徑的權和)。 由於遊戲的規定,幽香只能選擇一個空地做爲補給站。在遊戲的過程當中,幽香可能會在某些空地上製造一些軍隊,也可能會減小某些空地上的軍隊,進行了這樣的操做之後,出於經濟上的考慮,幽香每每能夠移動他的補給站從而省一些錢。可是因爲這個遊戲的地圖是在太大了,幽香沒法輕易的進行最優的安排,你能幫幫她嗎? 你能夠假定一開始全部空地上都沒有軍隊。
對於幽香的每一個操做,輸出操做完成之後,天天的最小花費,也即若是幽香選擇最優的補給點進行補給時的花費。
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 }
咱們再來一道大同小異的題目:
風見幽香有一個好朋友叫八雲紫,她們常常一塊兒看星星看月亮從詩詞歌賦談到
第一行三個用空格分開的數 n、Q和A,表示樹的大小、開店的方案個數和妖
對於每一個方案,輸出一行表示方便值。
知足 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 }
最後咱們來個大boss試試知難♂而上
強強和萌萌是一對好朋友。有一天他們在外面閒逛,忽然看到前方有一棵紫荊樹。這已是紫荊花飛舞的季節了,無數的花瓣以肉眼可見的速度從紫荊樹上長了出來。仔細看看的話,這個大樹其實是一個帶權樹。每一個時刻它會長出一個新的葉子節點。每一個節點上有一個可愛的小精靈,新長出的節點上也會同時出現一個新的小精靈。小精靈是很萌可是也很脆弱的生物,每一個小精靈 i 都有一個感覺能力值Ri ,小精靈 i, j 成爲朋友當且僅當在樹上 i 和 j 的距離 dist(i,j) ≤ Ri + R! ,其中 dist(i, j)表示在這個樹上從 i 到 j 的惟一路徑上全部邊的邊權和。強強和萌萌很好奇每次新長出一個葉子節點以後,這個樹上總共有幾對朋友。
咱們假定這個樹一開始爲空,節點按照加入的順序從 1開始編號。因爲強強很是好奇, 你必須在他每次出現新節點後立刻給出總共的朋友對數,不能拖延哦。
共有 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。
包含 n 行,每行輸出1 個整數, 表示加入第 i 個點以後,樹上有幾對朋友。
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 }
三.總結
點分治和動態樹分治實際上是對樹規思想的變形。咱們利用分治樹高度很小的優勢,對每一個點維護相應的信息,
經過$O(logn)$的額外複雜度來進行普通樹規沒法完成的操做。但願本文能對你有幫助!