cf題面.net
給一棵無根樹,每條邊有邊權。而後q個詢問,每次詢問給個w,求樹上有多少對點之間的路徑上的最大值小於等於w。code
離線。先把全部邊按照邊長升序排序,再把全部詢問按照w升序排序。排序
以後從小到大處理每一個詢問。對於一個詢問,首先因爲詢問已經排好序了,因此前一個答案是以前加的邊對於答案的貢獻,咱們就先把上一個詢問的答案直接複製過來,以後把小於等於這個詢問的w的全部邊加入到樹上,而後並查集更新答案:每加一條邊,對答案產生的貢獻是「這條邊兩端的連通塊」大小之積。ip
以後恢復順序,輸出,沒了。get
虛擬勝過程中看見這題的時候,想不到用並查集,而是想着深搜(相似CF1118 F1),對於一條邊,討論它下方的子樹和上方樹的其餘部分的狀況,但上方沒想出來怎麼處理,由於可能上方存在權值更大的邊,不能一整個乘下去……而後想到點分治樹分治啥的,全是xjb想……去看了這題的標籤,dsu(並查集)、分治、排序。開始不知道啥是dsu,去百度找到了個dsu on tree
,點進去發現時啓發式合併,和這個沒啥關係……iframe
#include<cstdio> #include<algorithm> const int MAXN=2e5+5; int n,m; struct Que{ int id,w; long long ans; }q[MAXN]; bool cmp1(Que & a,Que & b){return a.w<b.w;} bool cmp2(Que & a,Que & b){return a.id<b.id;} struct Edge{ int u,v,w; bool operator < (const Edge & x)const{ return w<x.w; } }e[MAXN]; int fa[MAXN],sz[MAXN]; int find(int x) { return fa[x]=fa[x]==x?x:find(fa[x]); } void uni(int x,int y) { x=find(x); y=find(y); fa[x]=y; sz[x]+=sz[y]; sz[y]=sz[x]; } int main() { // freopen("test.in","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); } for(int i=0;i<m;i++) { scanf("%d",&q[i].w); q[i].id=i; q[i].ans=0; } std::sort(e+1,e+n); std::sort(q,q+m,cmp1); for(int i=1;i<=n;i++) fa[i]=i,sz[i]=1; for(int i=0,pos=1;i<m;i++) { q[i].ans=q[i-1].ans; while(pos<n&&e[pos].w<=q[i].w) { int u=e[pos].u,v=e[pos].v; q[i].ans+=1LL*sz[find(u)]*sz[find(v)]; uni(u,v); pos++; } } std::sort(q,q+m,cmp2); for(int i=0;i<m;i++) printf("%lld ",q[i].ans); return 0; }