P5306 [COCI2019] Transport

题目

P5306 [COCI2019] Transport

分析

点分治+平衡树。

首先,我们很容易想到这里要使用点分治,然后我们可以考虑如何拼接路径。

发现因为这道题这样的话是有方向的,于是可以考虑维护两个数据结构,一个是从这个点出发,一个是在这个点结束。

那么具体怎么维护呢?我们发现对于一条路径是“在这里结束”类型的,它无论结果有多坏,只要记下它在路径上最坏的那一处,那么另外一条和它拼接的就只要能够使得最坏的那一处满足,那么剩下的肯定满足。

而对于“从这里开始”类型的,它就必须是一直满足条件的状态,然后可以统计当前到根的剩下的多余的油,这个是拿去拼接的“资本”。

由于这里的值域很大,我们不能直接树状数组了,于是可以考虑平衡树,这里使用了 (FHQ Treap)

那么拼接就很好维护了,但是,我们发现这样的点对是有序的,所以我们在拼接的时候,只需要正着反着都做一遍,答案就统计完了。

代码

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
    x=0;char ch=getchar();bool f=false;
    while(!isdigit(ch)) f|=ch=='-',ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x=f?-x:x;return;
}
template <typename T>
inline void write(T x){
    if(x<0) putchar('-'),x=-x;
    if(x>9) write(x/10);
    putchar(x%10^48);
	return;
}
#define ll long long
const int N=1e6+5;
const ll INF=1e18+7;
int n,a[N],idx,siz[N],vis[N],To[N],tmp,pool,root,w[N],Cnt,cnt;
ll Ans,path[N],Now;
struct FHQ_Treap{
	int siz,key,rs,ls;
	ll val;
	#define ls(x) t[x].ls
	#define rs(x) t[x].rs
	#define key(x) t[x].key
	#define val(x) t[x].val
	#define siz(x) t[x].siz
}t[N];
inline void Update(int x){siz(x)=siz(ls(x))+siz(rs(x))+1;return ;}
inline int NewNode(ll v){int x=++cnt;siz(x)=1,ls(x)=rs(x)=0,key(x)=rand(),val(x)=v;return x;}
void Split(int now,ll k,int &x,int &y){
	if(!now) return x=y=0,void();
	if(val(now)<=k) x=now,Split(rs(x),k,rs(x),y);
	else y=now,Split(ls(y),k,x,ls(y));
	Update(now);
	return ;
}
int Merge(int x,int y){
	if(!x||!y) return x|y;
	if(key(x)<=key(y)){rs(x)=Merge(rs(x),y);Update(x);return x;}
	else{ls(y)=Merge(x,ls(y));Update(y);return y;}
}
void Insert(ll v){int x,y;Split(root,v,x,y);root=Merge(Merge(x,NewNode(v)),y);return ;}
void Delete(ll v){int x,y,z;Split(root,v-1,x,y);Split(y,v,z,y);root=Merge(Merge(x,Merge(ls(z),rs(z))),y);return ;}
int Query(ll v){
	int x,y,z;
	Split(root,v-1,x,y);
	z=siz(y);
	root=Merge(x,y);
	return z;
}
bool Vis[N];
int head[N],nex[N],to[N],val[N];
void add(int u,int v,int w){
	nex[++idx]=head[u];
	to[idx]=v;
	val[idx]=w;
	head[u]=idx;
	return ;
}
int Root,FMax,Size;
void GetRoot(int x){
	Vis[x]=true;siz[x]=1;int Max=0;
	for(int i=head[x];i;i=nex[i]){
		int y=to[i];
		if(vis[y]||Vis[y]) continue;
		GetRoot(y);siz[x]+=siz[y];
		Max=max(Max,siz[y]);
	}
	Max=max(Max,Size-siz[x]);
	if(Max<FMax) FMax=Max,Root=x;
	Vis[x]=false;
	return ;
}
void GetPath1(int x,int fa,ll Max,ll now){
	Ans+=Query(Max);
	for(int i=head[x];i;i=nex[i]){
		int v=to[i];
		if(v==fa||vis[v]) continue;
		GetPath1(v,x,max(Max,now-a[x]+val[i]),now-a[x]+val[i]);
	}
}
void GetPath2(int x,int fa,ll Max,ll now){
	if(now>=Max) Insert(now),path[++Cnt]=now,Now++;
	for(int i=head[x];i;i=nex[i]){
		int v=to[i];
		if(v==fa||vis[v]) continue;
		GetPath2(v,x,max(now,Max),now+a[v]-val[i]);
	}
}
void Calc(int x){
	tmp=Cnt=Now=0;
	for(int i=head[x];i;i=nex[i]){
		int v=to[i];
		if(vis[v]) continue;
		To[++tmp]=v,w[tmp]=val[i];
	}
	for(int i=1;i<=tmp;i++){
		GetPath1(To[i],x,w[i],w[i]);
		GetPath2(To[i],x,a[x],a[x]+a[To[i]]-w[i]);
	}
	while(Cnt) Delete(path[Cnt--]);
	Insert(a[x]);
	for(int i=tmp;i>=1;i--){
		GetPath1(To[i],x,w[i],w[i]);
		GetPath2(To[i],x,a[x],a[x]+a[To[i]]-w[i]);
	}
	while(Cnt) Delete(path[Cnt--]);
	Delete(a[x]);
	Ans+=Now/2;
	return ;
}
void DFS(int x){
	Root=0,FMax=Size=siz[x];
	GetRoot(x);
	Calc(Root);
	vis[Root]=true;
	for(int i=head[Root];i;i=nex[i]){
		int y=to[i];
		if(vis[y]) continue;
		DFS(y);
	}
}
int main(){
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	int x,y,z;
	for(int i=1;i<n;i++){
		read(x),read(y),read(z);
		add(x,y,z),add(y,x,z);
	}
	Root=0,siz[1]=n;
	Insert(-(INF)),DFS(1);
	write(Ans);
	return 0;
}
原文地址:https://www.cnblogs.com/Akmaey/p/14726858.html