POJ1741 Tree
前言
今天刚刚学了点分治,赶紧把思路写一下,以防止以后忘记
分析
对于一个点,经过它的路径有两种可能,一是在一棵子树里(下图1-2-4),二是在两个子树里(下图2-1-5)。
那么对于情况二,我们可以把它拆成两条链相加。
对于情况一好像不行,但如果去分类讨论就会让程序更加复杂,但是发现它在它的子树里处理这种情况时就成了情况二,所以可以暂时不用管它,在递归下一层的时候返回来处理就行,不过需要加一个vis数组,防止情况搜多,这样就能够处理这种情况了。
但是有一种路径需要被减去,即合并(1-2-3和1-2-4)这种路径,因为显然不能合并啊,所以要减去。
减的方法是在往下递归的时候,以连向父节点的那条边的权值为起始值处理一遍答案,这种就相当于,模拟了一下从1搜下来的时候的情况,但是只搜这一棵树,就能够求出曾经多算的情况。
还有为了防止不会搜太多层,需要从重心开始搜。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int lqs=1e4+10;
struct Edge{
int to,nxt,val;
}e[lqs<<1];
int h[lqs],idx;
void Ins(int a,int b,int c){
e[++idx].to=b;e[idx].val=c;
e[idx].nxt=h[a];h[a]=idx;
}
int n,k,rt,T,siz[lqs],w[lqs];
bool vis[lqs];
void dfs1(int u,int fa){
siz[u]=1;w[u]=0;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v]||v==fa)continue;
dfs1(v,u);
siz[u]+=siz[v];
w[u]=max(w[u],siz[v]);
}
w[u]=max(w[u],T-siz[u]);
if(w[u]<w[rt])rt=u;
}
int a[lqs],hh;
void dfs2(int u,int fa,int d){
a[++hh]=d;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v]||v==fa)continue;
dfs2(v,u,d+e[i].val);
}
}
int ans;
int calc(int u,int d){
hh=0;
int sum=0;
dfs2(u,0,d);
sort(a+1,a+hh+1);
for(int i=1,j=hh;;i++){
while(j&&a[i]+a[j]>k)j--;
if(i>j)break;
sum+=j-i+1;
}
return sum;
}
void solve(int u){
ans+=calc(u,0);
vis[u]=1;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(vis[v])continue;
ans-=calc(v,e[i].val);
T=siz[v];rt=0;
dfs1(v,0);
solve(rt);
}
}
void init(){
memset(h,0,sizeof(h));
ans=rt=idx=0;
memset(vis,0,sizeof(vis));
}
int main(){
w[0]=0x7f7f7f7f;
while(~scanf("%d%d",&n,&k)&&n&&k){
init();
for(int i=1;i<n;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
Ins(a,b,c);Ins(b,a,c);
}
T=n;
dfs1(1,0);
solve(rt);
printf("%d\n",ans-n);
}
}
int - > long long
0 - > 100