题目概述
给出一棵树,求dis(x,y)<=K的点对(x,y)有多少,dis(x,y)表示x到y的最短距离(x<y)。
解题报告
这道题是明显的点分,就不阐述点分了,主要讲讲另一种解法:平衡树+启发式合并,效率也很不错:O(n*log2^2(n))。
说的这么高大上,实际上就是暴力啦。先找出根节点,然后Dfs递归处理。对于每一个节点,都建立一棵平衡树(作者蒟蒻,这里使用Treap),然后从叶往根上推(从叶往根上推根据Dfs的深度优先就可以轻松实现)。举个例子,如图: 首先先把son1的Treap的所有节点(直接遍历即可)和fa的Treap进行累加,累加后,把fa加入当son1中。处理完毕后,根据启发式合并的思想,先比较fa的Treap的平衡树大小和son2的Treap的平衡树大小,把小的往大的身上累加,然后把小的加入到大的中。以此类推,直到处理完所有son。
但是如何计算两个点之间的距离?其实很简单(当然也可以直接用lazy_tag,留给你们自己思考啦:P): 记录dis[i]表示i节点在这棵树中的深度。由图所示,son1和son2的距离就是dis[son1]+dis[son2]-2*dis[fa],不难发现2*dis[fa]可以独立领出来统一减去,那么问题就简化为dis[son1]+dis[son2]了。 ps:这样的话,就有一个细节了:fa不能在刚开始就加入Treap,否则统计会出错(fa和其他节点的距离是dis[son]-dis[fa],减去2*dis[fa]会出错)。
其实至此,这道题目就讲完了,再来谈谈启发式合并复杂度的问题:由于是小的加入到大的身上,所以原先小的树的节点所在的树的个数必定*2,而树的个数最多是n。所以每个节点顶多被加入log2(n)次,而每次加入的复杂度是log2(n),所以总时间复杂度为O(n*log2^2(n))。
示例程序
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn=
10005,maxm=
20005,maxt=
140005;
struct Treap
{
Treap *son[
2];
int x,w,si,p;
int cmp(
int k) {
if (k<x)
return 0;
else if (k>x)
return 1;
return -
1;}
void Pushup() {si=w+son[
0]->si+son[
1]->si;}
};
Treap tem[maxt],*null=&tem[
0],*len=null,*ro[maxn];
int n,K,son[maxm],nxt[maxm],lnk[maxn],w[maxm],E,ans;
bool vis[maxn];
void Add(
int x,
int y,
int z) {son[++E]=y;w[E]=z;nxt[E]=lnk[x];lnk[x]=E;}
Treap *newTreap(
int k,
int num) {len++;len->w=len->si=num;len->x=k;len->p=rand();len->son[
0]=len->son[
1]=null;
return len;}
void rotate(Treap* &
id,
int f)
{
Treap *t=
id->son[f^
1];
id->son[f^
1]=t->son[f];t->son[f]=
id;
id->Pushup();t->Pushup();
id=t;
}
void Insert(Treap* &
id,
int k,
int num)
{
if (
id==null) {
id=newTreap(k,num);
return;}
int f=
id->cmp(k);
id->si+=num;
if (f==-
1)
id->w+=num;
else
{
Insert(
id->son[f],k,num);
if (
id->son[f]->p>
id->p) rotate(
id,f^
1);
}
}
int Asksum(Treap*
id,
int k)
{
if (
id==null)
return 0;
int f=
id->cmp(k);
if (f==-
1)
return id->son[
0]->si+
id->w;
else
if (f==
0)
return Asksum(
id->son[
0],k);
else
if (f==
1)
return id->son[
0]->si+
id->w+Asksum(
id->son[
1],k);
}
void Join(Treap* &A,Treap* B)
{
if (B==null)
return;
Insert(A,B->x,B->w);
Join(A,B->son[
0]);Join(A,B->son[
1]);
}
int Count(Treap* A,Treap* B,
int dis)
{
if (B==null)
return 0;
return B->w*Asksum(A,dis-B->x)+Count(A,B->son[
0],dis)+Count(A,B->son[
1],dis);
}
void Dfs(
int x,
int dep)
{
vis[x]=
true;
for (
int j=lnk[x];j;j=nxt[j])
if (!vis[son[j]])
{
Dfs(son[j],dep+w[j]);
if (ro[x]->si<ro[son[j]]->si) swap(ro[x],ro[son[j]]);
ans+=Count(ro[x],ro[son[j]],K+
2*dep);Join(ro[x],ro[son[j]]);
}
ans+=Asksum(ro[x],K+dep);
Insert(ro[x],dep,
1);
}
int main()
{
freopen(
"program.in",
"r",stdin);
freopen(
"program.out",
"w",stdout);
for (scanf(
"%d%d",&n,&K);n||K;scanf(
"%d%d",&n,&K))
{
memset(lnk,
0,
sizeof(lnk));E=
0;memset(vis,
0,
sizeof(vis));ans=
0;
for (
int i=
1;i<=n;i++) ro[i]=null;len=null;
for (
int i=
1;i<=n-
1;i++)
{
int x,y,z;scanf(
"%d%d%d",&x,&y,&z);
Add(x,y,z);Add(y,x,z);
}
Dfs(
1,
0);
printf(
"%d\n",ans);
}
return 0;
}
转载请注明原文地址: https://ju.6miu.com/read-15989.html