Description
给定一棵N个结点的树,结点用正整数1..N编号,每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路径上经过边的权值和,其中要求 。将这N*(N-1)/2个距离值从大到小排序,输出前M个距离值。
对于100%的数据满足N<=50,000,M<=min{N*(N-1)/2,300,000}。
Solution
首先对这棵树点分治
如果对于每个分治中心的所有儿子都按顺序一个个插入,再排序,显然是不行的。
考虑把这个分治中心全部跑完排序完再放到一起做。
将这个分治中心的子树中的点按照DFS序排起来
可以发现对于一个点,能构成通过分治中心的路径的点(即不跟他在同一棵子树)体现在DFS序上就是左右两个区间。
那么用RMQ做出区间最大值,对于每个点加入大根堆,每次取堆顶加入维护答案的set,然后对顶的区间裂成两半区间,相应维护插回堆里即可。
复杂度
Code
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <queue>
#include <set>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define M 300005
#define LL long long
using namespace std;
int pr[2*N],cnt,fs[N],nt[2*N],dt[2*N],sz[N],n,m1,m,mi,mw,n1,lim,ans[M],d[M],dis[N],dfn[N],rmq[N][16],cf[16],l2[N],fr[N],dfw[N];
bool bz[N];
multiset<int> h;
bool cmp(int x,int y)
{
return x>y;
}
struct node
{
int l,r,x,w,v;
friend bool operator <(node x,node y)
{
return x.v<y.v;
}
};
priority_queue<node> h1;
void link(int x,int y,int z)
{
nt[++m1]=fs[x];
dt[fs[x]=m1]=y;
pr[m1]=z;
}
void find(int k,int fa,int num)
{
sz[k]=1;
int ms=0;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa&&!bz[p])
{
find(p,k,num);
sz[k]+=sz[p];
ms=max(ms,sz[p]);
}
}
if(max(num-sz[k],ms)<mi) mi=max(num-sz[k],ms),mw=k;
}
void dfs(int k,int fa,int s)
{
if(fa==0) fr[k]=0;
else if(fr[fa]==0) fr[k]=k;
else fr[k]=fr[fa];
dis[k]=s;
dfn[dfw[k]=++dfn[0]]=k;
sz[k]=1;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(p!=fa&&!bz[p])
{
dfs(p,k,s+pr[i]);
sz[k]+=sz[p];
}
}
}
int get(int l,int r)
{
int c=l2[r-l+1];
return ((dis[dfn[rmq[l][c]]]>dis[dfn[rmq[r-cf[c]+1][c]]])?rmq[l][c]:rmq[r-cf[c]+1][c]);
}
void doit(int k)
{
bz[k]=1;
int id=0;
dfn[0]=0;
dfs(k,0,0);
fo(i,1,dfn[0]) rmq[i][0]=i;
fo(j,1,15)
{
fo(i,1,dfn[0])
{
rmq[i][j]=(dis[dfn[rmq[i][j-1]]]>dis[dfn[rmq[i+cf[j-1]][j-1]]])?rmq[i][j-1]:rmq[i+cf[j-1]][j-1];
}
}
while(!h1.empty()) h1.pop();
int vc=0;
fo(i,2,dfn[0])
{
node p1;
p1.l=1,p1.r=dfw[fr[dfn[i]]]-1;
p1.x=dfn[i];
if(p1.l<=p1.r)
{
p1.w=get(p1.l,p1.r);
p1.v=dis[dfn[i]]+dis[dfn[p1.w]];
h1.push(p1);
vc++;
}
}
if(!h1.empty())
{
node px=h1.top();
while(cnt<m||px.v>(*h.begin()))
{
if(cnt==m) h.erase(h.begin());
else cnt++;
h.insert(px.v);
h1.pop();
vc--;
node p1=px,p2=px;
p1.r=px.w-1;
p2.l=px.w+1;
if(p1.l<=p1.r)
{
p1.w=get(p1.l,p1.r);
p1.v=dis[p1.x]+dis[dfn[p1.w]];
h1.push(p1);
vc++;
}
if(p2.l<=p2.r)
{
p2.w=get(p2.l,p2.r);
p2.v=dis[p2.x]+dis[dfn[p2.w]];
h1.push(p2);
vc++;
}
if(h1.empty()) break;
px=h1.top();
}
}
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
if(!bz[p])
{
mi=n+1;
find(p,k,sz[p]);
doit(mw);
}
}
}
int main()
{
cin>>n>>m;
cf[0]=1;
fo(i,1,15) cf[i]=cf[i-1]*2,l2[cf[i]]=i;
fo(i,1,n) if(!l2[i]) l2[i]=l2[i-1];
fo(i,1,n-1)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
link(x,y,z),link(y,x,z);
}
mi=n+1;
find(1,0,n);
doit(mw);
for(multiset<int>::iterator it=h.begin();it!=h.end();it++) ans[++ans[0]]=*it;
fod(i,ans[0],1) printf("%d\n",ans[i]);
}