POJ1987 Distance Statistics——树的分治——Pku1987

这道题是楼教主《男人八题》中的一题,算法:树的分治。

看到题目时很容易想到直接Dfs,但那样的话时间复杂度会高达O(n^2)!对于n<=40000的数据来说根本无法承受。所以,必须考虑分治的思想。

怎么分治呢?

树的重心!

至于树的重心,不熟悉的OIer可以做掉POJ1655,或者NOI2011 Day2的第一题来练手,这两道题要求的就是树的重心。至于pascal语言的ACMer可以考虑编译开关,OIer最好不要加编译开关,练一练非递归手写栈也是不错的。

基本思路:

1、把这棵无根树以1为根节点,使其变成一棵有根树。

2、对于每棵现在要处理的树,进行如下处理:

(1)遍历这棵树,找到所有一端点为根,路径长度<=k的路径总数

(2)通过(1)求出的结果求和计算出所有长度<=k的的路径总数

(3)去重:也就是删去(2)中计算有重合的路径部分

(4)找到这棵子树的重心,递归处理这棵子树,也就是重复步骤2

3、统计答案并输出

总体来说,这题难度不小,可是把它做掉的感觉也是很cool的。最重要的是,树的分治思想在这道题中体现得淋漓尽致,这是这道题的借鉴意义所在。

POJ1741也是这个思想,可以拿来练手。

 

CODE

Program POJ1987;//By_Poetshy

Const

	maxn=40005;

Var

	i,j,k,m,n,d,kol                         :Longint;

	sum,root,min,tot,ans                    :Longint;

	pre,other,last,data		        		:Array[0..maxn*2]of Longint;

	seq										:Array[0..maxn*2]of Longint;

	size,f                                  :Array[0..maxn*2]of Longint;

	v										:Array[0..maxn]of Boolean;



Procedure Getroot(i:Longint);

var j,k,w:Longint;

begin

	j:=last[i];

	size[i]:=1;

	v[i]:=true;

	w:=0;

	while j<>0 do

		begin

			k:=other[j];

			if not v[k] then

				begin

					Getroot(k);

					inc(size[i],size[k]);

					if size[k]>w then w:=size[k];

				end;

			j:=pre[j];

		end;

	if sum-size[i]>w then w:=sum-size[i];

	if w<min then

		begin

			min:=w;

			root:=i;

		end;

	v[i]:=false;

end;



Procedure Count(i:Longint);

var j,k:Longint;

begin

	v[i]:=true;

	j:=last[i];

	inc(sum);

	while j<>0 do

		begin

			k:=other[j];

			if not v[k] then Count(k);

			j:=pre[j];

		end;

	v[i]:=false;

end;



Procedure Deal(i,d:Longint);

var j,k:Longint;

begin

	if d<=kol then

		begin

			inc(tot);

			seq[tot]:=d;

		end else exit;

	v[i]:=true;

	j:=last[i];

	while j<>0 do

		begin

			k:=other[j];

			if not v[k] then Deal(k,d+data[j]);

			j:=pre[j];

		end;

	v[i]:=false;

end;



Procedure Qsort(l,r:Longint);

var i,j,k,temp:Longint;

begin

        if l>=r then exit;

	i:=l;j:=r;k:=seq[(i+j)>>1];

	repeat

		while seq[i]<k do inc(i);

		while seq[j]>k do dec(j);

		if i<=j then

			begin

				temp:=seq[i];

				seq[i]:=seq[j];

				seq[j]:=temp;

				inc(i);dec(j);

			end;

	until i>j;

	if i<r then Qsort(i,r);

	if l<j then Qsort(l,j);

end;



Function Reduce(l,r:Longint):Longint;

var i,j:Longint;

begin

	Reduce:=0;

	j:=r;

	for i:=l to r do

		begin

                        if i=j then exit;

			while (i<j)and(seq[i]+seq[j]>kol)do dec(j);

			if i=j then exit;

			inc(Reduce,j-i);

		end;

end;



Procedure Dfs(i:Longint);

var y,j,k,la:Longint;

begin

	sum:=0;tot:=1;seq[tot]:=0;

	Count(i);

	if sum=1 then exit;

	min:=sum;

	Getroot(i);

        i:=root;j:=last[i];

        v[i]:=true;

	while j<>0 do

		begin

			k:=other[j];

			if not v[k] then

				begin

					la:=tot+1;

					Deal(k,data[j]);

					Qsort(la,tot);

					dec(f[i],Reduce(la,tot));

				end;

			j:=pre[j];

		end;

        Qsort(1,tot);

	inc(f[i],Reduce(1,tot));

	j:=last[i];

	while j<>0 do

		begin

			k:=other[j];

			if not v[k] then Dfs(k);

			j:=pre[j];

		end;

	v[i]:=false;

end;



BEGIN

	readln(n);

	for i:=1 to n-1 do

		begin

			readln(m,j,d);

			inc(k);pre[k]:=last[m];last[m]:=k;other[k]:=j;data[k]:=d;

			inc(k);pre[k]:=last[j];last[j]:=k;other[k]:=m;data[k]:=d;

		end;

	readln(kol);

	Dfs(1);

        ans:=0;

	for i:=1 to n do

                inc(ans,f[i]);

        writeln(ans);

END.

你可能感兴趣的:(poj)