UOJ#53. 【UR #4】追击圣诞老人 树链剖分 k短路

原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ53.html

题意

  给定一棵有 n 个节点的树。

  每一个点有一个权值。

  对于每一个 $i$ 给定三个参数 $a_i,b_i,c_i$ ,从第 $i$ 个点出发下一步能到达的点 x 需要满足以下三个要求之一:

  1. x 在 $a_i$ 到 $b_i$ 的简单路径上。 

  2. x 在 $a_i$ 到 $c_i$ 的简单路径上。

  3. x 在 $c_i$ 到 $b_i$ 的简单路径上。

 

  问从任意一个点出发,经过的节点的权值和前 k 小的路线长度为多少。注意可以重复经过节点。

  空间限制 100MB,时间限制 3s

  $n,k\leq 5\times 10^5$

  保证答案小于 $10^8$。

题解

  由于 $k,n$ 同阶,所以后面的复杂度分析不区分 $n$ 和 $k$ 。

  首先把每一个点可以到的节点拆成不超过两条链。

  用树链剖分+线段树+预处理重链前缀min ,来实现 $O(\log n)$ 求一条链上的权值最小点。

  如果我们暴力的来,那么显然可以:

    初始直接把所有点都扔到一个堆里面,以节点权值和为关键字,依次取出最小元素,每取出一个,就在堆中加入从这个点再走一步可以得到的方案,这样只需要取出 k 次就好了。

  显然时空复杂度萎了。

  于是我们考虑在树链剖分、线段树上打这些标记,这些标记的关键字是“从当前状态下,再向该区间中权值最小的点走得到的路径长度”,从堆中取出的时候分裂标记,就可以得到一个时间复杂度 $O(n\log^2 n)$ 空间复杂度 $O(n\log n)$ 的做法,仍然不足以通过此题。

  考虑将标记的形式改为 $(a,b)$ ,即一条链上的两个端点。那么取出这条链的时候,只需要分两部分更新堆状态就好了:

  1. 从这条链的最小权值点处分裂这条链,变成两条更短的链,加入堆中。

  2. 从这条链的最小权值点出发,最多可以到达两条链,加入堆中。

  于是我们就得到了一个空间复杂度 $O(n)$ ,时间复杂度 $O(n\log n)$ 的做法。

  

  然后由于博主人傻常数大,还是被卡空间了。

  于是强行把vector数组写成数组模拟链表,把系统堆写成手写堆……

  最后突然发现他保证答案小于 1e8 ,不是边权小于 1e8 的时候我惊呆了。

  之前卡了这么多常数,其实只需要把存路径长度的数据类型从 longlong 改成 int 就好了……

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL read(){
	LL x=0,f=0;
	char ch=getchar();
	while (!isdigit(ch))
		f|=ch=='-',ch=getchar();
	while (isdigit(ch))
		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return f?-x:x;
}
const int N=500005;
int n,k;
struct Info{
	int a,b,c,d;
}v[N];
struct Gragh{
	static const int M=N*2;
	int cnt,y[M],nxt[M],fst[N];
	void clear(){
		cnt=1;
		memset(fst,0,sizeof fst);
	}
	void add(int a,int b){
		y[++cnt]=b,nxt[cnt]=fst[a],fst[a]=cnt;
	}
}g;
int w[N];
int fa[N],depth[N],size[N],son[N],top[N],I[N],O[N],aI[N],Min[N],Time=0;
void dfs1(int x,int pre,int d){
	depth[x]=d,fa[x]=pre,son[x]=0,size[x]=1;
	for (int i=g.fst[x];i;i=g.nxt[i]){
		int y=g.y[i];
		if (y!=pre){
			dfs1(y,x,d+1);
			size[x]+=size[y];
			if (!son[x]||size[y]>size[son[x]])
				son[x]=y;
		}
	}
}
void ckwMin(int &x,int y){
	if (w[x]>w[y])
		x=y;
}
void dfs2(int x,int Top){
	top[x]=Top,aI[I[x]=++Time]=x;
	if (son[x])
		ckwMin(Min[son[x]],Min[x]),dfs2(son[x],Top);
	for (int i=g.fst[x];i;i=g.nxt[i]){
		int y=g.y[i];
		if (y!=fa[x]&&y!=son[x])
			dfs2(y,y);
	}
	O[x]=Time;
}
int LCA(int x,int y){
	int fx=top[x],fy=top[y];
	while (fx!=fy){
		if (depth[fx]<depth[fy])
			swap(fx,fy),swap(x,y);
		x=fa[fx],fx=top[x];
	}
	return depth[x]<depth[y]?x:y;
}
int isanc(int x,int y){
	return I[x]<=I[y]&&I[y]<=O[x]; 
}
int go_up(int x,int y){
	if (isanc(aI[I[y]+1],x))
		return aI[I[y]+1];
	int fx=top[x];
	while (depth[fx]>depth[y]){
		x=fx;
		if (depth[fa[x]]>depth[y])
			x=fa[x],fx=top[x];
		else
			break;
	}
	return x;
}
namespace Seg{
	int v[N<<2];
	void build(int rt,int L,int R){
		if (L==R)
			return (void)(v[rt]=aI[L]);
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		build(ls,L,mid);
		build(rs,mid+1,R);
		ckwMin(v[rt]=v[ls],v[rs]);
	}
	int query(int rt,int L,int R,int xL,int xR){
		if (xL<=L&&R<=xR)
			return v[rt];
		int mid=(L+R)>>1,ls=rt<<1,rs=ls|1;
		if (xR<=mid)
			return query(ls,L,mid,xL,xR);
		if (xL>mid)
			return query(rs,mid+1,R,xL,xR);
		int res=query(ls,L,mid,xL,xR);
		ckwMin(res,query(rs,mid+1,R,xL,xR));
		return res;
	}
	int query(int x,int y){
		int ans=x,fx=top[x],fy=top[y];
		while (fx!=fy){
			if (depth[fx]<depth[fy])
				swap(fx,fy),swap(x,y);
			ckwMin(ans,Min[x]);
			x=fa[fx],fx=top[x];
		}
		if (depth[x]>depth[y])
			swap(x,y);
		ckwMin(ans,query(1,1,n,I[x],I[y]));
		return ans;
	}
}
const int Size=N*5;
struct Node{
	int a,b,len;
	Node(){}
	Node(int _a,int _b,int pre){
		a=_a,b=_b;
		len=pre+w[Seg :: query(a,b)];
	}
}s[Size];
int stt=0;
bool cmp(int a,int b){
	return s[a].len>s[b].len;
}
struct priority_Queue{
	int v[Size],s;
	bool empty(){
		return s==0;
	}
	void up(int x){
		int y=x>>1;
		while (y){
			if (cmp(v[y],v[x]))
				swap(v[x],v[y]),x=y,y=x>>1;
			else
				break;
		}
	}
	void down(int x){
		int y=x<<1;
		while (y<=s){
			if (y<s&&cmp(v[y],v[y+1]))
				y|=1;
			if (cmp(v[x],v[y]))
				swap(v[x],v[y]),x=y,y=x<<1;
			else
				break;
		}
	}
	void pop(){
		swap(v[1],v[s--]);
		down(1);
	}
	int top(){
		return v[1];
	}
	void push(int x){
		v[++s]=x;
		up(s);
	}
}Q;
void push(Node x){
	s[++stt]=x,Q.push(stt);
}
void Split(Node x,int Mi){
	int a=x.a,b=x.b,m=Mi;
	if (!isanc(m,a))
		swap(a,b);
	if (m!=a){
		int aa=go_up(a,m);
		push(Node(a,aa,x.len-w[m]));
	}
	if (m!=b){
		int bb;
		if (isanc(m,b))
			bb=go_up(b,m);
		else
			bb=fa[m];
		push(Node(b,bb,x.len-w[m]));
	}
}
void solve(){
	while (!Q.empty())
		Q.pop();
	for (int i=1;i<=n;i++)
		push(Node(i,i,0));
	while (k--){
		Node now=s[Q.top()];
		Q.pop();
		printf("%d\n",now.len);
		int x=Seg :: query(now.a,now.b);
		Split(now,x);
		if (~v[x].a)
			push(Node(v[x].a,v[x].b,now.len));
		if (~v[x].c)
			push(Node(v[x].c,v[x].d,now.len));
	}
}
int main(){
	n=read(),k=read();
	for (int i=1;i<=n;i++)
		w[i]=read(),Min[i]=i;
	g.clear();
	for (int i=2;i<=n;i++){
		int a=read();
		g.add(a,i);
		g.add(i,a);
	}
	for (int i=1;i<=n;i++)
		v[i].a=read(),v[i].b=read(),v[i].c=read();
	dfs1(1,0,0);
	dfs2(1,1);
	for (int i=1;i<=n;i++){
		int a=v[i].a,b=v[i].b,c=v[i].c;
		v[i].a=v[i].b=v[i].c=v[i].d=-1;
		if (depth[c]<depth[b])
			swap(b,c);
		if (depth[b]<depth[a])
			swap(a,b);
		if (depth[c]<depth[b])
			swap(b,c);
		if (a==b||b==c){
			v[i].a=a,v[i].b=c;
			continue;
		}
		int ab=LCA(a,b),ac=LCA(a,c),bc=LCA(b,c);
		if (ab==ac&&ab==bc){
			int g=ab;
			if (depth[a]>depth[g]){
				v[i].a=a,v[i].b=b;
				v[i].c=c,v[i].d=go_up(c,g);
				continue;
			}
			v[i].a=b,v[i].b=c;
			continue;
		}
		if (ab==bc)
			swap(a,b),swap(ac,bc);
		else if (ac==bc)
			swap(a,c),swap(ab,bc);
		if (depth[c]<depth[b])
			swap(b,c),swap(ab,ac);
		if (isanc(b,c)){
			v[i].a=a,v[i].b=c;
			continue;
		}
		v[i].a=a,v[i].b=b;
		int d=go_up(c,bc);
		v[i].c=c,v[i].d=d;
	}
	Seg :: build(1,1,n);
	solve();
	return 0;
}

  

 

posted @ 2018-12-27 12:08  zzd233  阅读(361)  评论(0编辑  收藏  举报