題目連接:http://www.lydsy.com/JudgeOnline/problem.php?id=4012php
題意概述:給出一顆N點的樹,保證樹上全部點的度不超過3,樹上每一個點有權值,每條邊有權值,如今有Q組詢問,每組給出信息u,L,R,問點權在區間[L,R]的點到點u的距離和爲多少。強制在線。ios
N<=150000,Q<=200000.spa
可能這是我這幾天作過的題裏面最水可是最碼的一個了。。。。code
實際上看見樹上全部點的度不超過3就感受能夠用邊分治作,具體的思路實際上很簡單,創建一個邊分治結構,這個結構大約有logN層,每層有N個點,對於每組詢問,看詢問的點u在當前樹被分紅的兩棵子樹中的哪一棵,統計出另外一個子樹中全部點權在詢問區間內的點到u的距離,而後遞歸到u所在的子樹,最終就能夠計算出答案。blog
重點在於實現,一不當心處理的時候就會多寫出幾個常數出來。。。(不要問我是怎麼知道的)排序
實際上這個題我yy的東西重點就在於把邊分治創建成一個二叉樹的結構而後在裏面去搞事情(小火車萬歲!),最多有2N個分治結構(每條邊會產生2個,有N-1條邊)。每一層每一個點只會有一個信息,利用這個性質能夠記錄不少東西,詢問的時候在分治結構中遞歸,令當前分治結構中詢問點所在的子樹根節點爲x,另外一邊的子樹根節點爲y(實際上就是斷掉的邊的兩個端點),每一次詢問的時候就在y的信息裏面二分計算詢問區間內點到y的距離,同時獲得這些點的數量,而後把從y通過x到u的距離補全累加到答案中,遞歸。(要點已經交代完了,剩下的請各位本身yy,手動滑稽)遞歸
預處理能夠用歸併排序作到O(nlogn),主要複雜度在查詢,單次是O(log2n)。get
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #include<queue> #include<set> #include<map> #include<vector> #include<cctype> using namespace std; const int maxn=150005; const int maxd=45; typedef long long LL; int N,Q,A,L,R,age[maxn]; LL ans,dist[maxd][maxn],sum[maxd][maxn]; struct edge{ int to,next,w; }E[maxn<<1]; int first[maxn],np,sz[maxn],rt[maxd][maxn]; int ID[maxd][maxn],cnt; int ke[maxn<<1],MAX,ch[maxn<<1][2],l[maxn<<1],r[maxn<<1],size[maxd],tot[maxn<<1]; bool vis[maxn<<1]; void _scanf(int &x) { x=0; char cha=getchar(); while(cha<'0'||cha>'9') cha=getchar(); while(cha>='0'&&cha<='9') x=x*10+cha-'0',cha=getchar(); } int out_cnt,out[22]; void _printf(LL x) { out_cnt=0; out[++out_cnt]=x%10,x/=10; while(x) out[++out_cnt]=x%10,x/=10; while(out_cnt) putchar('0'+out[out_cnt--]); } void add_edge(int u,int v,int w) { E[++np]=(edge){v,first[u],w}; first[u]=np; } void data_in() { _scanf(N);_scanf(Q);_scanf(A); for(int i=1;i<=N;i++) _scanf(age[i]); int x,y,z; for(int i=1;i<N;i++){ _scanf(x);_scanf(y);_scanf(z); add_edge(x,y,z); add_edge(y,x,z); } } void DFS(int i,int f,int SZ,int id,int d,LL l) { sz[i]=1,size[d]++,dist[d][i]=l; for(int p=first[i];p;p=E[p].next){ int j=E[p].to; if(j==f||vis[p]) continue; rt[d][j]=rt[d][i]; DFS(j,i,SZ,id,d,l+E[p].w); sz[i]+=sz[j]; int tmp=max(sz[j],SZ-sz[j]); if(tmp<MAX) MAX=tmp,ke[id]=p; } } void merge_sort(int x,int d) { int pos=l[x],i=l[ch[x][0]],j=r[ch[x][0]],r1=r[ch[x][0]],r2=r[ch[x][1]]; while(i<r1&&j<r2) ID[d][pos++]=age[ID[d+1][i]]<age[ID[d+1][j]]?ID[d+1][i++]:ID[d+1][j++]; while(i<r1) ID[d][pos++]=ID[d+1][i++]; while(j<r2) ID[d][pos++]=ID[d+1][j++]; } void div_tree(int i,int SZ,int id,int d) { MAX=SZ; l[id]=size[d],rt[d][i]=i; DFS(i,0,SZ,id,d,0); r[id]=size[d]; if((tot[id]=SZ)==1){ ID[d][l[id]]=i; return; } int o0=E[ke[id]].to,o1=E[(ke[id]-1^1)+1].to; int _sz0=sz[o0],_sz1=SZ-sz[o0]; vis[ke[id]]=vis[(ke[id]-1^1)+1]=1; div_tree(o0,_sz0,ch[id][0]=++cnt,d+1); div_tree(o1,_sz1,ch[id][1]=++cnt,d+1); merge_sort(id,d); sum[d][l[id]]=dist[d][ID[d][l[id]]]; for(int j=l[id]+1;j<r[id];j++) sum[d][j]=sum[d][j-1]+dist[d][ID[d][j]]; } bool cmp(int x,int y){ return age[x]<age[y]; } void solve(int p,int id,int d) { if(tot[id]==1) return; int x=E[ke[id]].to,y=E[(ke[id]-1^1)+1].to; int dd=rt[d][p]!=x,t=ch[id][dd^1]; age[0]=L; int ll=lower_bound(ID[d]+l[t],ID[d]+r[t],0,cmp)-ID[d]-1; LL v1=ll>=l[t]?sum[d][ll]:0; age[0]=R; int rr=upper_bound(ID[d]+l[t],ID[d]+r[t],0,cmp)-ID[d]-1; LL v2=rr>=l[t]?sum[d][rr]:0; ans+=v2-v1+(rr-ll)*(E[ke[id]].w+dist[d][p]); solve(p,ch[id][dd],d+1); } void work() { div_tree(1,N,++cnt,1); int u,a,b; for(int i=1;i<=Q;i++){ _scanf(u);_scanf(a);_scanf(b); L=min((a+ans)%A,(b+ans)%A); R=max((a+ans)%A,(b+ans)%A); ans=0; solve(u,1,2); _printf(ans),putchar('\n'); } } int main() { data_in(); work(); return 0; }