樹的點分治
本題目是他講的第一個例題;
個人理解:每次都找樹的重心,計算以重心爲根的子樹之間所貢獻的答案。不斷這樣下去;若是這棵樹是一條鏈,那麼就和快排,歸併的線性分治法同樣了。若是不是一條鏈那麼就至關於,選中一個點,標記爲使用過。而後樹會被這個節點劃分紅多棵子樹。而後這樣分治下去。思想好理解。可是代碼不是很好想!詳見註解。
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
typedef long long int LL;
const int maxn=20000+100;
struct Node
{
int to,val,next;
}edge[maxn];
int first[maxn],sz,ans,idx,root,n,k;
bool vis[maxn];
/* ----------------------*/
/**
* 鄰接表部分
*/
void init()
{
memset(first,-1,sizeof(first));
memset(vis,0,sizeof(vis));
sz=0;
}
void addedge(int s,int t,int val)
{
edge[sz].val=val,edge[sz].to=t,edge[sz].next=first[s];
first[s]=sz++;
}
/*****
* mi,用來找樹的重心的時候做比較用的;
* mx[i]數組 表明i節點的子樹中最大的size;
* size[i]表明i節點的子樹的節點數量;
* dis[i]表明i好節點到根的深度;
* ****************************/
int mi,mx[maxn],size[maxn],dis[maxn];
void dfssize(int x,int pre) //x節點這個子樹求size,mx;
{
size[x]=1;
mx[x]=0;
for(int i=first[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to,val=edge[i].val;
if(to!=pre&&!vis[to])
{
dfssize(to,x);
size[x]+=size[to];
if(size[to]>mx[x]) mx[x]=size[to];
}
}
}
void dfsroot(int rt,int x,int pre) //找rt這個 子樹的重心
{
mx[x]=max(mx[x],size[rt]-size[x]);
if(mx[x]<mi) mi=mx[x],root=x;
for(int i=first[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to;
if(to!=pre&&!vis[to]) dfsroot(rt,to,x);
}
}
void dfsdis(int x,int pre,int dd) //計算子樹 x的 dis 數組;
{
dis[idx++]=dd;
for(int i=first[x];i!=-1;i=edge[i].next)
{
int to=edge[i].to,val=edge[i].val;
if(to!=pre&&!vis[to]) dfsdis(to,x,dd+val);
}
}
/**
* 計算 F(x)=(子樹x的 depth(i)+depth(j)<=k 的對數;)
* calc(x,d);當d=0;返回的是x這顆子樹F(x),
* 等d=某條邊權值,calc(x,d) 返回值表明子樹之間的F(x)
*/
int calc(int x,int d)
{
int res=0;
idx=0;
dfsdis(x,x,d);
sort(dis,dis+idx);
int i=0,j=idx-1;
while(i<j)
{
while(dis[i]+dis[j]>k&&i<j) j--;
res+=j-i;
i++;
}
return res;
}
void dfs(int x)
{
mi=n;
dfssize(x,x);
dfsroot(x,x,x);
ans+=calc(root,0);//當前節點
vis[root]=1;
for(int i=first[root];i!=-1;i=edge[i].next)
{
int to=edge[i].to,val=edge[i].val;
if(!vis[to])
{
ans-=calc(to,val);//減去當前節點的子樹之間的
// printf(">> calc=%d\n",calc(root,val));
dfs(to);
}
}
}
int main()
{
while(scanf("%d%d",&n,&k)!=EOF)
{
if(n==0&&k==0) break;
init();
for(int i=1;i<n;i++)
{
int x,y,val;
scanf("%d%d%d",&x,&y,&val);
addedge(x,y,val);
addedge(y,x,val);
}
ans=0;
dfs(1);
printf("%d\n",ans);
}
return 0;
}