传送门(权限题)
大致题意:给定一个N个结点的树,结点用正整数1..N编号。每条边有一个正整数权值。用d(a,b)表示从结点a到结点b路边上经过边的权值。其中要求a<b.将这n*(n-1)/2个距离从大到小排序,输出前M个距离值。
此题可看成树上的“超级钢琴”。
如果当前分治中心的dfs序为x,那么最左边子树的dfs序区间为[x+1,r1],第二个为[r1+1,r2]。。。以此类推。
每个子树都和它前面的点匹配:比如[r1+1,r2]的这些点就和[x,r1]的这些点匹配。(含分治中心)然后[r2+1,r3]的这些点和[x,r2]的匹配。也就是说在同一个分治中心,左边界是不变的,随着dfs的进行,右边界在变化。
我们记录每个点到分治中心的距离,现在将问题重新理一下:
两个点确定一条路径(该路径一定经过分治中心),而对于一个固定的点,它的另一个端点有一个范围(通过点分治序来体现)。我们现在要求前若干大的路径。这就和超级钢琴是一样的问题了。
看一下具体实现:
dis数组下标为点分治序,记录对应点到分治中心的距离。搜完一个子树后,R要往后移动。
premax记录前面子树出现的最长距离,tmp记录当前子树的最长距离。跑完一个子树后更新一下premax。
inline void solve(int u){
vis[u]=1,dis[L=++tot]=0,premax=0;
for(int i=Head[u];i;i=Next[i]) if(!vis[V[i]]) tmp=0,R=tot,getdis(V[i],u,W[i],premax),premax=max(tmp,premax);
for(int i=Head[u];i;i=Next[i]) if(!vis[V[i]]) root=0,SZ=sz[V[i]],getroot(V[i],u),solve(root);
}
我用q数组记录“超级和弦”。也就是右端点固定,左端点可动的不定路径。我们把这些超级和弦丢进优先队列里。
这里说一下:node是一个四元组,l,r,sum,id分别记录左端点左边界的点分治序、左端点右边界的点分治序、范围内的最长路长度、当前点的点分治序。
void getdis(int u,int f,int sum,int pre){
dis[++tot]=sum,tmp=max(tmp,sum),Q.push(node(L,R,sum+pre,tot));
for(int i=Head[u];i;i=Next[i])if(GG)getdis(V[i],u,sum+W[i],pre);
}
我在这里犯了个错误:
void getdis(int u,int f,int len,int edge){
dis[++tot]=len+edge,q[++num]=node(L,R,0,tot);
for(int i=Head[u];i;i=Next[i])if(GG)getdis(V[i],u,dis[tot],W[i]);
}
之前写getdis写成了这样。。。这里tot是会变的。
搜过一个子树后,dis[tot]就变成了前一个子树的最后一个节点到当前分治中心的距离。。。。然后距离就炸了,,半天没看出来。。。233。。把dis[tot]改成len+edge就对了。
#include<bits/stdc++.h>
#define GG ((V[i]!=f)&&(!vis[V[i]]))
#define K int(log2(r-l+1))
using namespace std;
const int maxn=5e4+10;
const int maxm=maxn*20;
int root=0,tot=0,SZ,vis[maxn],sz[maxn],mx[maxn],dis[maxm],st[maxm][20],premax=0,tmp=0;
int n,m,u,v,w,L,R,Head[maxn],V[maxn<<1],Next[maxn<<1],W[maxn<<1],cnt=0,num=0,d1,d2,pos;
struct node{int l,r,sum,id;}p;priority_queue<node> Q;
inline bool operator<(const node &a,const node &b){return a.sum<b.sum;}
inline void add(int u,int v,int w){++cnt,Next[cnt]=Head[u],W[cnt]=w,V[cnt]=v,Head[u]=cnt;}
inline int MAX(int a,int b){return (dis[a]>dis[b])?(a):(b);}
inline int query(int l,int r){return (l>r)?(-1):(MAX(st[l][K],st[r-(1<<K)+1][K]));}
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
void getroot(int u,int f){
sz[u]=1,mx[u]=0;
for(int i=Head[u];i;i=Next[i])if(GG)
getroot(V[i],u),sz[u]+=sz[V[i]],mx[u]=max(mx[u],sz[V[i]]);
mx[u]=max(mx[u],SZ-sz[u]); if(mx[root]>mx[u]) root=u;
}
void getdis(int u,int f,int sum,int pre){
dis[++tot]=sum,tmp=max(tmp,sum),Q.push((node){L,R,sum+pre,tot});
for(int i=Head[u];i;i=Next[i])if(GG)getdis(V[i],u,sum+W[i],pre);
}
inline void solve(int u){
vis[u]=1,dis[L=++tot]=0,premax=0;
for(int i=Head[u];i;i=Next[i]) if(!vis[V[i]]) tmp=0,R=tot,getdis(V[i],u,W[i],premax),premax=max(tmp,premax);
for(int i=Head[u];i;i=Next[i]) if(!vis[V[i]]) root=0,SZ=sz[V[i]],getroot(V[i],u),solve(root);
}
inline void pre_st(){
for(int i=0;(1<<i)<=tot;++i) for(int j=1;(((1<<i)+j-1)<=tot);++j)
if(i) st[j][i]=MAX(st[j][i-1],st[j+(1<<(i-1))][i-1]); else st[j][i]=j;
}
int main(){
SZ=mx[0]=n=read(),m=read();
for(int i=1;i<n;++i) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
getroot(1,0),solve(root),pre_st();
for(int i=1;i<=m;++i){
p=Q.top(),Q.pop(),printf("%d\n",p.sum);
pos=query(p.l,p.r),d1=query(p.l,pos-1),d2=query(pos+1,p.r);
if(d1!=-1) Q.push((node){p.l,pos-1,dis[p.id]+dis[d1],p.id});
if(d2!=-1) Q.push((node){pos+1,p.r,dis[p.id]+dis[d2],p.id});
}
}
压行真的很爽。