【NOIP2018】【洛谷P5024】保卫王国(动态DP)(全局平衡二叉树)

传送门


闲的蛋疼用全局平衡二叉树写了一遍这道题,跑得飞快。

动态DP基础题,并不想写题解。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;
		while(!isdigit(c=gc()));T num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;

cs ll INF=1e13;

cs int N=1e5+7;

int n,m;

struct matrix{
	ll a[2][2];
	matrix(){}
	
	ll *operator[](int of){return a[of];}
	cs ll *operator[](int of)cs{return a[of];}
	
	friend matrix operator*(cs matrix &A,cs matrix &B){
		matrix C;
		C[0][0]=std::min(A[0][0]+B[0][0],A[0][1]+B[1][0]);
		C[0][1]=std::min(A[0][0]+B[0][1],A[0][1]+B[1][1]);
		C[1][0]=std::min(A[1][0]+B[0][0],A[1][1]+B[1][0]);
		C[1][1]=std::min(A[1][0]+B[0][1],A[1][1]+B[1][1]);
		return C;
	}
};

int nxt[N<<1],to[N<<1],last[N],ecnt;
inline void adde(int u,int v){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}

int siz[N],son[N];
void pre_dfs(int u,int p){
	siz[u]=1;
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])if(v!=p){
		pre_dfs(v,u),siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}

int cost[N];
namespace GBT{
	ll val[N][2];
	int rt,son[N][2],fa[N],lsiz[N];
	matrix w[N];
	
	int st[N],top;
	
	inline void pushup(int u){
		w[u][1][0]=w[u][1][1]=val[u][1];
		w[u][0][1]=val[u][0],w[u][0][0]=INF;
		w[u]=w[son[u][1]]*w[u]*w[son[u][0]];
	}
	
	inline void ins(int u,int v){
		ll nt=std::min(w[v][1][0],w[v][1][1]);
		ll hv=std::min(std::min(w[v][0][0],w[v][0][1]),nt);
		val[u][0]+=nt;val[u][1]+=hv;
	}
	
	inline void del(int u,int v){
		ll nt=std::min(w[v][1][0],w[v][1][1]);
		ll hv=std::min(std::min(w[v][0][0],w[v][0][1]),nt);
		val[u][0]-=nt,val[u][1]-=hv;
	}
	
	inline int subbuild(int l,int r){
		if(l==r){pushup(st[l]);return st[l];}if(l>r)return 0;
		int tot=0;for(int re i=l;i<=r;++i)tot+=lsiz[st[i]];
		for(int re i=l,now=lsiz[st[i]];i<=r;now+=lsiz[st[++i]])
		if(now*2>=tot){
			int lc=son[st[i]][0]=subbuild(l,i-1);
			int rc=son[st[i]][1]=subbuild(i+1,r);
			fa[lc]=fa[rc]=st[i];pushup(st[i]);
			return st[i];
		}
		cerr<<"error 1\n";
		assert(0);
	}
	
	inline int build(int u){
		for(int re p=u;p;p=::son[p])lsiz[p]=siz[p]-siz[::son[p]];
		for(int re p=u;p;p=::son[p])
		for(int re e=last[p],v=to[e];e;v=to[e=nxt[e]])if(!lsiz[v]){
			v=build(v),fa[v]=p;ins(p,v);
		}top=0;
		for(int re p=u;p;p=::son[p])st[++top]=p,pushup(p);
		std::reverse(st+1,st+top+1);return subbuild(1,top);
	}
	
	inline bool isrt(int u){return son[fa[u]][1]!=u&&son[fa[u]][0]!=u;} 
	inline void modify(int u,int tu,ll add){
		val[u][!tu]+=add;
		while(u){
			if(fa[u]&&isrt(u)){
				del(fa[u],u);
				pushup(u);
				ins(fa[u],u);
			}
			else pushup(u);
			u=fa[u];
		}
	} 
	
	inline void query(int u,int tu,int v,int tv){
		modify(u,tu,INF);modify(v,tv,INF);
		ll ans=std::min(std::min(w[rt][0][0],w[rt][0][1]),std::min(w[rt][1][0],w[rt][1][1]));
		cout<<(ans>=INF?-1:ans)<<"\n";
		modify(u,tu,-INF);modify(v,tv,-INF);
	}
	
	inline void init(){
		for(int re i=1;i<=n;++i)val[i][1]=cost[i];
		w[0][0][1]=w[0][1][0]=INF;
		rt=build(1);
	}
}

signed main(){
#ifdef zxyoi
	freopen("kingdom.in","r",stdin);
#endif
	n=gi(),m=gi();gi();
	for(int re i=1;i<=n;++i)cost[i]=gi();
	for(int re i=1;i<n;++i)adde(gi(),gi());
	pre_dfs(1,0);GBT::init();
	while(m--){
		int u=gi(),tu=gi(),v=gi(),tv=gi();
		GBT::query(u,tu,v,tv);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值