標籤:print ret tree algo 最小 樹形dp ons 接下來 turn
點分治【bzoj1468】 Tree
Description
給你一棵TREE,以及這棵樹上邊的距離.問有多少對點它們兩者間的距離小於等於K
Input
N(n<=40000) 接下來n-1行邊描述管道,按照題目中寫的輸入 接下來是k
Output
一行,有多少對點之間的距離小於等於k
點分治開始入門。
點分治,主要是解決形如:給你一棵樹,求樹上滿足XX條件的點對的對數。
所以說應對的問題很多時候都和樹形DP相同。
首先告訴自己,分治是高效的演算法。
想一下,平時在面對普通的分治問題,每次肯定都是半分,直到成為小問題,然後再分別解決。
為了保證點分治的高效,所以我們每一次應該將當前問題分成最平均的兩個問題,放到樹上就是指我們要將當前的樹分成大小最平均的幾棵。
那麼就可以引入一個概念:樹的重心。定義是在樹上找一個點作為根,使得子樹中的size最大者最小,這樣我們就可以很好的將樹平均分。
所以解決點分治問題的基本思路也就有了:
? 我們從整棵樹開始,每一次找到當前樹的重心並且以他為根,也就是將無根樹轉成有根樹,然後對於當前的樹,我們只對與當前樹的根的點對進行處理。
? 對於這道題來說,就是找到路徑經過當前根的點對,去統計這些點對中合格數量對答案作出貢獻。
? 然後對於每個子樹,向下分治,繼續找重心……
code
#include<iostream>#include<cstdio>#include<algorithm>using namespace std;const int wx=40017;inline int read(){ int sum=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();} return sum*f;}int n,m,ans,num,root,k,tmp;int head[wx],dis[wx],size[wx];int f[wx],vis[wx];int temp[wx];struct e{ int nxt,to,dis;}edge[wx*2];void add(int from,int to,int dis){ edge[++num].nxt=head[from]; edge[num].to=to; edge[num].dis=dis; head[from]=num;}void getroot(int u,int fa){ size[u]=1;f[u]=0; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(v==fa||vis[v])continue; getroot(v,u); size[u]+=size[v]; f[u]=max(f[u],size[v]); } f[u]=max(f[u],tmp-size[u]); if(f[root]>f[u])root=u;}void dfs(int u,int fa){ temp[++temp[0]]=dis[u]; for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(v==fa||vis[v])continue;dis[0]++; dis[v]=dis[u]+edge[i].dis; dfs(v,u); }}int calc(int u,int now){ dis[u]=now;temp[0]=0;dfs(u,0); int l=1,r=temp[0];int re=0; sort(temp+1,temp+temp[0]+1); while(l<r){ if(temp[r]+temp[l]<=k)re+=r-l,l++; else r--; } return re;}void slove(int u){ vis[u]=1;ans+=calc(u,0); for(int i=head[u];i;i=edge[i].nxt){ int v=edge[i].to; if(vis[v])continue; ans-=calc(v,edge[i].dis); root=0;tmp=size[v];getroot(v,0);slove(root); }}int main(){ n=read(); for(int i=1;i<n;i++){ int x,y,z; x=read();y=read();z=read(); add(x,y,z);add(y,x,z); } k=read(); f[0]=(1<<30);tmp=n; getroot(1,0); slove(root); printf("%d\n",ans); return 0;}
點分治【bzoj1468】 Tree