【UOJ#284】快乐游戏鸡(长链剖分)(单调栈)

传送门


我TM就随手一写

UOJ速度榜rk4的同时,加上读优之后UOJ码长榜第二,把fread优化去掉后就成码长榜第一了???

某种意义上从侧面证明了这个做法的优越性

题解:

考虑什么时候能够从 s s s走到 t t t,当且仅当死亡次数已经达到了路径上的 w w w最大值,这个可以倍增求一下。

也就是说在死亡次数达到 max ⁡ w \max w maxw之前,我们只能一直死,那么显然我们希望能够早死早超生。

维护一个子树内部按照深度排好序后对于 w w w的单调栈。显然每次直接从栈顶贪心直到死亡次数足够,然后再加上从 s s s走到 t t t的距离就是我们要的答案。

于是我们需要求单调栈从栈顶到栈底的前缀和,但是显然我们只能维护后缀和。

栈的合并实际上按照任意顺序时间复杂度都是 O ( n ) O(n) O(n),但是我们需要注意空间,为了空间小点,我们长链剖分之后按照DFS序分配空间即可。

用指针会非常好写,数组其实应该也挺好写

对于每个询问,利用二分找到单调栈会弹到哪里,然后xjb算一下答案即可。


代码:

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

namespace IO{
	inline char gc(){
		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;T num;
		while(!isdigit(c=gc()));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;
using pii=std::pair<int,int>;
#define fi first
#define se second

cs int N=3e5+7;

int n,q;
std::vector<int> G[N];
std::vector<pii> qry[N];
int fa[20][N],mx[20][N];
int w[N],son[N],mxd[N],d[N];

void dfs1(int u,int p){
	d[u]=d[p]+1;fa[0][u]=p;mx[0][u]=w[p];
	for(int re i=0;fa[i][u];++i)
	fa[i+1][u]=fa[i][fa[i][u]],
	mx[i+1][u]=std::max(mx[i][fa[i][u]],mx[i][u]);
	for(int v:G[u]){dfs1(v,u);
	if(mxd[v]>mxd[son[u]])son[u]=v;}
	mxd[u]=mxd[son[u]]+1;
}

inline int query(int u,int to){int res=0;
	for(int re i=19;~i;--i)
	if(fa[i][u]&&d[fa[i][u]]>d[to])
	res=std::max(res,mx[i][u]),u=fa[i][u];
	return res;
}

ll ans[N];

struct node{int d,w;ll sum;};
node p[N],arr[N];
node *h[N],*t[N],*tp;

inline void ins(int u,node c){
	node *&h=::h[u],*&t=::t[u];
	while(h<=t&&h->w<=c.w)++h;
	if(h>t||h->d>c.d){
		c.sum=h<=t?
			(ll)(h->w-c.w)*h->d+h->sum:0ll;
		*--h=c;
	}
}

inline void merge(int u,int v){
	node *&h1=::h[u],*&t1=::t[u];
	node *&h2=::h[v],*&t2=::t[v];
	tp=arr;while(h1<=t1&&h1->d<t2->d)*++tp=*h1++;
	while(tp>arr&&h2<=t2)t2->d>tp->d?ins(u,*t2--):ins(u,*tp--);
	while(tp>arr)ins(u,*tp--);while(t2>=h2)ins(u,*t2--);
}

inline ll solve(int u,int v){
	node *l=h[u],*r=t[u],*mid;int w=query(v,u);
	while(l<r)(mid=l+(r-l)/2)->w<w?l=mid+1:r=mid;
	bool op=h[u]->w<=w;ll res=d[v]-d[u];
	res+=l->d*(ll)(w-(op?l->w:0))-(ll)d[u]*w;
	if(op)res+=h[u]->sum-l->sum+(ll)h[u]->w*h[u]->d;
	return res;
}

int dfn;
void dfs2(int u){++dfn;
	if(son[u])dfs2(son[u]),h[u]=h[son[u]],t[u]=t[son[u]];
	else h[u]=p+dfn,t[u]=h[u]-1;
	for(int v:G[u])if(v!=son[u])
	dfs2(v),merge(u,v);
	for(pii t:qry[u])ans[t.se]=solve(u,t.fi);
	ins(u,(node){d[u],w[u],0});
}

signed main(){
#ifdef zxyoi
	freopen("game.in","r",stdin);
#endif
	n=gi();
	for(int re i=1;i<=n;++i)w[i]=gi();
	for(int re i=2;i<=n;++i)G[gi()].push_back(i);
	q=gi();for(int re i=1;i<=q;++i){
		int s=gi(),t=gi();
		qry[s].push_back(pii(t,i));
	}dfs1(1,0),dfs2(1);
	for(int re i=1;i<=q;++i)cout<<ans[i]<<"\n";
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值