luogu4556 雨天的尾巴 (線段樹合並+差分)
阿新 • • 發佈:2018-09-04
() 次數 一場 main pre 輸出 return 時間 mes
題目背景
深繪裏一直很討厭雨天。
灼熱的天氣穿透了前半個夏天,後來一場大雨和隨之而來的洪水,澆滅了一切。
雖然深繪裏家鄉的小村落對洪水有著頑固的抵抗力,但也倒了幾座老房子,幾棵老樹被連根拔起,以及田地裏的糧食被弄得一片狼藉。
無奈的深繪裏和村民們只好等待救濟糧來維生。
不過救濟糧的發放方式很特別。
題目描述
首先村落裏的一共有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
他們的lca和lca的父節點對祖先們的貢獻是-1
最後統計答案時用每個點的子節點通過合並線段樹的方式更新該節點的信息。。。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int n,m,cnt,tot,maxn; int pr[100005]; int ans[100005]; int head[100005]; int rt[100005]; int fa[100005]; int dep[100005]; int siz[100005]; int son[100005]; int grand[100005]; struct node{ int u; int v; int z; int zx; }ac[100005];struct Tree{ int ls; int rs; int num; int mx; }tr[5500005]; struct Edge{ int fr; int to; int nxt; }edge[200005]; int cmp(node a,node b){ return a.z<b.z; } void init(){ memset(head,-1,sizeof(head)); } void addedge(int f,int t){ cnt++; edge[cnt].fr=f; edge[cnt].to=t; edge[cnt].nxt=head[f]; head[f]=cnt; } void dfs1(int p){ for(int i=head[p];i!=-1;i=edge[i].nxt){ int e=edge[i].to; if(e==fa[p])continue; fa[e]=p; dep[e]=dep[p]+1; dfs1(e); siz[p]+=siz[e]; if(siz[e]>siz[son[p]])son[p]=e; } } void dfs2(int p){ if(p!=son[fa[p]])grand[p]=p; else grand[p]=grand[fa[p]]; for(int i=head[p];i!=-1;i=edge[i].nxt){ int e=edge[i].to; if(e==fa[p])continue; dfs2(e); } } int lca(int x,int y){ while(grand[x]!=grand[y]){ if(dep[grand[x]]<dep[grand[y]])swap(x,y); x=fa[grand[x]]; } if(dep[x]>dep[y])swap(x,y); return x; } void update(int o){ if(tr[tr[o].ls].num>=tr[tr[o].rs].num){ tr[o].num=tr[tr[o].ls].num; tr[o].mx=tr[tr[o].ls].mx; }else { tr[o].num=tr[tr[o].rs].num; tr[o].mx=tr[tr[o].rs].mx; } } void build(int &o,int l,int r,int p,int val){ if(!o)o=++tot; if(l==r){ tr[o].num+=val; if(tr[o].num)tr[o].mx=l; else tr[o].num=0; return; } int mid=(l+r)>>1; if(p>mid)build(tr[o].rs,mid+1,r,p,val); else build(tr[o].ls,l,mid,p,val); update(o); } void Merge(int &a,int b,int l,int r){ if(!b)return; if(!a){ a=b; return; } if(l==r){ tr[a].num+=tr[b].num; tr[a].mx=l; return; } int mid=(l+r)>>1; Merge(tr[a].ls,tr[b].ls,l,mid); Merge(tr[a].rs,tr[b].rs,mid+1,r); update(a); } void dfs(int p){ for(int i=head[p];i!=-1;i=edge[i].nxt){ int e=edge[i].to; if(e==fa[p])continue; dfs(e); Merge(rt[p],rt[e],1,maxn); } pr[p]=tr[rt[p]].mx; } int main(){ init(); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ siz[i]=1; } for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); addedge(u,v); addedge(v,u); } dfs1(1); dfs2(1); for(int i=1;i<=m;i++){ scanf("%d%d%d",&ac[i].u,&ac[i].v,&ac[i].z); } sort(ac+1,ac+m+1,cmp); for(int i=1;i<=m;i++){ if(ac[i].z!=ac[i-1].z)maxn++; ac[i].zx=maxn; ans[maxn]=ac[i].z; } for(int i=1;i<=m;i++){ int f=lca(ac[i].u,ac[i].v); int ff=fa[f]; build(rt[ac[i].u],1,maxn,ac[i].zx,1); build(rt[ac[i].v],1,maxn,ac[i].zx,1); build(rt[f],1,maxn,ac[i].zx,-1); if(ff)build(rt[ff],1,maxn,ac[i].zx,-1); } dfs(1); for(int i=1;i<=n;i++){ printf("%d\n",ans[pr[i]]); } return 0; }
luogu4556 雨天的尾巴 (線段樹合並+差分)