【BZOJ】3784: 树上的路径-点分治序+ST表

本文探讨了如何通过点分治策略将树上问题转化为序列问题,利用优先队列维护最优前k步解决方案。针对特定问题,如bzoj3784和bzoj2006,介绍了如何在点分治序上寻找最优组合,使用ST表进行区间最大值维护,实现高效的算法优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门:bzoj3784


题解

k k k大比较好做(之前看成前 k k k小了)

所有的最优前k步问题最终都转成了bzoj2006: [NOI2010]超级钢琴

如何将序列上问题转到树上,并维护本质不同的拓展呢?

考虑点分治,把树上问题转回点分治序上的问题:
对于序列中的每个点 i i i,权值 v i = d i s ( v i , i 的 分 治 重 心 ) v_i=dis(v_i,i的分治重心) vi=dis(vi,i),对于 i i i可与其组合的点 j j j满足:

  1. 属于同一个分治重心
  2. i , j i,j i,j不在分治重心的某个子结点的同一子树内
    这样的 j j j就是在点分治序上是连续的两段,由于 ( i , j ) , ( j , i ) (i,j),(j,i) (i,j),(j,i)等价,所以固定某个方向的一段即可。

在优先队列中维护这样的四元组 ( u , l , r , p ) (u,l,r,p) (u,l,r,p)表示点 u u u可选范围为 [ l , r ] [l,r] [l,r]且其中值最大的为 v p v_p vp,按 v i + v p v_i+v_p vi+vp降序排序。

拓展 ( u , l , r , p ) → ( u , l , p − 1 , m a x ( l , p − 1 ) ) , ( u , p + 1 , r , m a x ( p + 1 , r ) ) (u,l,r,p)\to (u,l,p-1,max(l,p-1)),(u,p+1,r,max(p+1,r)) (u,l,r,p)(u,l,p1,max(l,p1)),(u,p+1,r,max(p+1,r))
S T ST ST表维护区间最大值。


代码

#include<bits/stdc++.h>
using namespace std;
const int N=50010,M=1e6+10;

int n,m,d[M],mx[20][M],bel[M],brt[M],lg[M];
int head[N],to[N<<1],nxt[N<<1],w[N<<1],tot;
int df[N],ot[N],sz[N],dfn,S,MN,rt,nw;
bool vs[N];

inline int ask(int l,int r)
{
	int bs=lg[r-l+1],x=mx[bs][l],y=mx[bs][r-(1<<bs)+1];
	return d[x]>d[y]?x:y;
}

struct P{
    int u,l,r,p,v;
    P(){};
    P(int u_,int l_,int r_){u=u_;l=l_;r=r_;p=ask(l,r);v=d[u]+d[p];};
    bool operator <(const P&ky)const{return v<ky.v;}
}dg;
priority_queue<P>que;

inline void lk(int u,int v,int vv)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=vv;}

void fdrt(int x,int fr)
{
	int i,j,mxx=0;sz[x]=1;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fr || vs[j]) continue;
		fdrt(j,x);sz[x]+=sz[j];
		mxx=max(mxx,sz[j]);
	}
	mxx=max(mxx,S-sz[x]);
	if(mxx<MN) MN=mxx,rt=x;
}

void dfs(int x,int fr,int dis)
{
	int i,j;
	d[(df[x]=++dfn)]=dis;
	brt[dfn]=nw;mx[0][dfn]=dfn;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fr || vs[j]) continue;
		dfs(j,x,dis+w[i]);
	}
	ot[df[x]]=dfn;
}

void col(int x,int fr)
{
	int i,j;bel[df[x]]=nw;
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(j==fr || vs[j]) continue;
		col(j,x);
	}
}

void divide(int x)
{
	int i,j,sum=S;vs[x]=true;
	nw=dfn+1;dfs(x,0,0);
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(vs[j]) continue;
		nw=df[j];col(j,x);
	}
	for(i=head[x];i;i=nxt[i]){
		j=to[i];if(vs[j]) continue;
		S=sz[j]<sz[x]?sz[j]:sum-sz[x];MN=N;
		fdrt(j,x);divide(rt);
	}
}

int main(){
	int i,j,x,y,z;
	scanf("%d%d",&n,&m);
	for(i=1;i<n;++i){scanf("%d%d%d",&x,&y,&z);lk(x,y,z);lk(y,x,z);}
	S=n;MN=N;fdrt(1,0);divide(rt);
	for(i=2;i<=dfn;++i) lg[i]=lg[i>>1]+1;
	for(j=1;(1<<j)<=dfn;++j)
		for(i=1;i+(1<<j)-1<=dfn;++i){
			x=mx[j-1][i];y=mx[j-1][i+(1<<(j-1))];
			mx[j][i]= d[x]>d[y]?x:y;
		}
	for(i=1;i<=dfn;++i) if(bel[i]){
		if(brt[i]<bel[i]) que.push(P(i,brt[i],bel[i]-1));
//		if(ot[brt[i]]>ot[bel[i]]) que.push(P(i,ot[bel[i]]+1,ot[brt[i]]));
	}
	for(;m;--m){
		dg=que.top();que.pop();printf("%d\n",dg.v);
		if(dg.l<dg.p) que.push(P(dg.u,dg.l,dg.p-1));
		if(dg.r>dg.p) que.push(P(dg.u,dg.p+1,dg.r));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值