P4556 [Vani有約會]雨天的尾巴(樹上差分+線段樹合並)
題目背景
深繪裏一直很討厭雨天。
灼熱的天氣穿透了前半個夏天,後來一場大雨和隨之而來的洪水,澆滅了一切。
雖然深繪裏家鄉的小村落對洪水有著頑固的抵抗力,但也倒了幾座老房子,幾棵老樹被連根拔起,以及田地裏的糧食被弄得一片狼藉。
無奈的深繪裏和村民們只好等待救濟糧來維生。
不過救濟糧的發放方式很特別。
題目描述
首先村落裏的一共有n座房屋,並形成一個樹狀結構。然後救濟糧分m次發放,每次選擇兩個房屋(x,y),然後對於x到y的路徑上(含x和y)每座房子裏發放一袋z類型的救濟糧。
然後深繪裏想知道,當所有的救濟糧發放完畢後,每座房子裏存放的最多的是哪種救濟糧。
輸入輸出格式
輸入格式:第一行兩個正整數n,m,含義如題目所示。
接下來n-1行,每行兩個數(a,b),表示(a,b)間有一條邊。
再接下來m行,每行三個數(x,y,z),含義如題目所示。
n行,第i行一個整數,表示第i座房屋裏存放的最多的是哪種救濟糧,如果有多種救濟糧存放次數一樣,輸出編號最小的。
如果某座房屋裏沒有救濟糧,則對應一行輸出0。
輸入輸出樣例
輸入樣例#1: 復制5 3
1 2
3 1
3 4
5 3
2 3 3
1 5 2
3 3 3
輸出樣例#1: 復制
2
3
3
0
2
說明
對於20%的數據,1 <= n, m <= 100
對於50%的數據,1 <= n, m <= 2000
對於100%的數據,1 <= n, m <= 100000, 1 <= a, b, x, y <= n, 1 <= z <= 100000
Vani
這道題看到了樹上路徑的操作,很容易就想到了樹剖,可是這個題中,每一個樹上的節點下面都有klogk個節點,樹剖的鏈上操作並不能快速的修改每個點內的每種顏色的權值線段樹,所以說樹剖就解決不了這個問題,
這其實就可以用差分來解決這個問題,一看到鏈上操作,最後統計答案,自然而然的想到樹上差分
,a++ ,b++,lca--,fa[lca]--就可以完成一條鏈的操作。
一開始可以想,我們每個節點上開一個cnt數組表示i這個數字出現了多少次,那麽節點i上出現最多的數字就是cnt數組的最大值
那麽我們求i節點的cnt數組可以暴力的把它的所有孩子的cnt數組按位相加起來來進行求解,然後如果這個節點上有插入或者刪除數字的操作我們再對cnt數組進行幾次操作就行了~
不過這樣太慢了……發現這個東西慢主要是數組按位相加這個操作,所以我們可以用線段樹合並來實現數組按位相加這個操作
復雜度o(能過)
不會證明,玄學的復雜度,說是o(nlogn+n)
1 #include<iostream>
2 #include<cstdio>
3 #include<algorithm>
4 #define N 100002
5 using namespace std;
6 int p[N][21],head[N],tot,deep[N],tr[N*50],A[N*50],L[N*50],R[N*50],num[N],rt[N],top,ans[N],toy;
7 struct zzh{
8 int n,to;
9 }e[N<<1];
10 struct bi{
11 int a,b,d;
12 }q[N];
13 inline void add(int u,int v){
14 e[++tot].n=head[u];
15 e[tot].to=v;
16 head[u]=tot;
17 }
18 void dfs(int u,int fa){
19 for(int i=1;(1<<i)<=deep[u];++i)p[u][i]=p[p[u][i-1]][i-1];
20 for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
21 int v=e[i].to;p[v][0]=u;deep[v]=deep[u]+1;
22 dfs(v,u);
23 }
24 }
25 inline int getlca(int a,int b){
26 if(deep[a]<deep[b])swap(a,b);
27 int k=deep[a]-deep[b];
28 for(int i=20;i>=0;--i)if(k>=(1<<i))a=p[a][i],k-=(1<<i);
29 if(a==b)return a;
30 for(int i=20;i>=0;--i)if(p[a][i]!=p[b][i])a=p[a][i],b=p[b][i];
31 return p[a][0];
32 }
33 void upd(int &cnt,int l,int r,int x,int y){
34 if(!cnt)cnt=++toy;
35 if(l==r){
36 tr[cnt]+=y;
37 if(tr[cnt]>0)A[cnt]=num[l];
38 else A[cnt]=0;
39 return;
40 }
41 int mid=(l+r)>>1;
42 if(mid>=x)upd(L[cnt],l,mid,x,y);
43 else upd(R[cnt],mid+1,r,x,y);
44 tr[cnt]=max(tr[L[cnt]],tr[R[cnt]]);
45 if(tr[L[cnt]]>=tr[R[cnt]])A[cnt]=A[L[cnt]];
46 else A[cnt]=A[R[cnt]];
47 }
48 int query(int &cnt,int l,int r){
49 if(!cnt)return 0;// 這個意思是這個點根本就沒開,當然返回0了,我把這句刪了就T了,為啥呢??
50 return A[cnt];
51 if(l==r)return num[l];
52 int mid=(l+r)>>1;
53 if(tr[L[cnt]]>=tr[R[cnt]])return query(L[cnt],l,mid);
54 else return query(R[cnt],mid+1,r);
55 }
56 int merge(int &u,int v,int l,int r){
57 if(!u||!v)return u+v;
58 if(l==r){
59 tr[u]+=tr[v];
60 if(tr[u])A[u]=num[l];
61 else A[u]=0;
62 return u;
63 }
64 int mid=(l+r)>>1;
65 L[u]=merge(L[u],L[v],l,mid);
66 R[u]=merge(R[u],R[v],mid+1,r);
67 tr[u]=max(tr[L[u]],tr[R[u]]);
68 if(tr[L[u]]>=tr[R[u]])A[u]=A[L[u]];
69 else A[u]=A[R[u]];
70 return u;
71 }
72 void dfs2(int u,int fa){
73 for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa){
74 int v=e[i].to;
75 dfs2(v,u);
76 rt[u]=merge(rt[u],rt[v],1,top);
77 }
78 ans[u]=query(rt[u],1,top);
79 }
80 int main(){
81 int n,m,u,v;
82 scanf("%d%d",&n,&m);
83 for(int i=1;i<n;++i)scanf("%d%d",&u,&v),add(u,v),add(v,u);
84 dfs(1,0);
85 for(int i=1;i<=m;++i)scanf("%d%d%d",&q[i].a,&q[i].b,&q[i].d),num[++top]=q[i].d;
86 sort(num+1,num+top+1);
87 top=unique(num+1,num+top+1)-num-1;
88 for(int i=1;i<=m;++i){
89 q[i].d=lower_bound(num+1,num+top+1,q[i].d)-num;
90 int lca=getlca(q[i].a,q[i].b);
91 upd(rt[q[i].a],1,top,q[i].d,1);upd(rt[q[i].b],1,top,q[i].d,1);
92 upd(rt[lca],1,top,q[i].d,-1);upd(rt[p[lca][0]],1,top,q[i].d,-1);
93 }
94 dfs2(1,0);
95 for(int i=1;i<=n;++i)printf("%d\n",ans[i]);
96 return 0;
97 }
P4556 [Vani有約會]雨天的尾巴(樹上差分+線段樹合並)