[IOI2021]地牢游戏

2021/11/17 23:13:58

本文主要是介绍[IOI2021]地牢游戏,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

地牢游戏

题解

首先,我们根据 s u b t a s k   3 , 4 subtask\,3,4 subtask3,4,应该很容易想到一种倍增的做法去解决我们的问题。
对于这几个图,我们的分层是相当少的,所以我们可以将我们整个过程分成至多 6 6 6个阶段,对于每个阶段单独倍增直到超越这个阶段,或者说抵达我们的终点。

我们考虑将我们的倍增做法延伸到我们的正解上,显然,由于当我们胜利后会直接加上一个 s i s_i si​,所以可以发现这实际上是一个加倍的过程,我们先前打不掉,然后变得打得掉最多只会经过 log ⁡   S \log\,S logS层。
我们可以把我们的整个段化划分成 log ⁡   S \log\,S logS个 [ 2 k , 2 k + 1 ) [2^k,2^{k+1}) [2k,2k+1)的区间,每次进入这个区间后我们就看成我们可以 w i n win win下所有 s < 2 k s< 2^k s<2k的点,会输掉所有 s ⩾ 2 k s\geqslant 2^k s⩾2k的点。
我们一直倍增到我们跑到一个可以打赢的 [ 2 k , 2 k + 1 ) [2^k,2^{k+1}) [2k,2k+1)点的位置,显然,这种情况我们就可以跳出这个区间了。或者我们的值超过 2 k + 1 2^{k+1} 2k+1,亦或者我们抵达了我们的终点为止。
判断我们是否跑到一个可以打赢的 [ 2 k , 2 k + 1 ) [2^k,2^{k+1}) [2k,2k+1)点位置只需要记录下我们初始值是多少时能够战胜我们面前的该点,这东西显然也是可以倍增处理的。

这样的话,预处理和询问都是带个 O ( log ⁡ 2 n ) O\left(\log^2n\right) O(log2n),不说时间,就是空间也是会炸的,我们考虑优化。
显然,对于一个 [ 2 k , 2 k + 1 ) [2^k,2^{k+1}) [2k,2k+1),真正重要的是我们所有的在 [ 2 k , 2 k + 1 ) [2^k,2^{k+1}) [2k,2k+1)内的关键点。
我们可以只对于这些点建立倍增数组,这样的话每个点就只需要建立一个倍增数组。
对于一个不在当前区间内的点,我们就暴力处理出这个点在这个区间内跑时能够到的第一个关键点。
当然,如果一个点在跑的过程中就大于我们的 2 k + 1 2^{k+1} 2k+1了,我们可以把它扔到下一个 k + 1 k+1 k+1的图上去处理,显然,当它大于后它跑的规则就是下一个图的规则了。
当然不是找到那些非关键点中它超越范围的点,而是就是当前关键点的下一个点或者我们的起始点,因为非关键点跑的规则在我们大于我们的 2 k + 1 2^{k+1} 2k+1时是与我们当前图是一样的,我们之后的过程中也不会经过我们的关键点了,自然可以这样处理。
当然,我们在每个图中都可以将终点看成一个关键点,方便转移,只是不需要处理终点的倍增数组罢了。

时间复杂度 O ( n log ⁡   n + q log ⁡ 2 n ) O\left(n\log\,n+q\log^2n\right) O(nlogn+qlog2n),空间复杂度 O ( n log ⁡   n ) O\left(n\log\,n\right) O(nlogn),由于笔者开了 4 4 4个大小为 n log ⁡   n n\log\,n nlogn的数组,空间基本是卡着的。

源码

#include "dungeons.h"
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
typedef vector<int> vi;
#define MAXN 400005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
const LL INF=0x3f3f3f3f3f3f;
int n,f[MAXN][25],nxt[MAXN][25],s[MAXN],p[MAXN];
LL dis[MAXN][25],mx[MAXN][25],dp[MAXN],g[MAXN][25],w[MAXN],l[MAXN];
vector<pii>G[MAXN];vi tmp;queue<int>qq;
void init(int _n,vi _s,vi _p,vi _w,vi _l){
	n=_n;
	for(int i=0;i<n;i++)s[i]=_s[i];
	for(int i=0;i<n;i++)p[i]=_p[i];
	for(int i=0;i<n;i++)w[i]=_w[i];
	for(int i=0;i<n;i++)l[i]=_l[i];
	for(int i=n-1;i>=0;i--)dp[i]=dp[w[i]]+s[i];
	for(int i=0;i<24;i++)f[n][i]=n;w[n]=l[n]=n;
	for(int i=0;i<24;i++){
		for(int j=0;j<=n;j++)dis[j][i]=INF,G[j].clear();
		for(int j=0;j<n;j++)
			if(s[j]>=(1<<i+1))G[l[j]].pb(mkpr(j,p[j]));
			else if(s[j]<(1<<i))G[w[j]].pb(mkpr(j,s[j]));
			else qq.push(j),dis[j][i]=0,nxt[j][i]=j;
		qq.push(n);dis[n][i]=0;nxt[n][i]=n;
		while(!qq.empty()){
			int u=qq.front(),siz=G[u].size();qq.pop();
			for(int j=0;j<siz;j++){
				int v=G[u][j].fir,wp=G[u][j].sec;qq.push(v);
				dis[v][i]=dis[u][i]+wp;nxt[v][i]=nxt[u][i];
			}
		}
		tmp.clear();
		for(int j=0;j<n;j++)
			if((1<<i)<=s[j]&&s[j]<(1<<i+1))
				f[j][0]=nxt[l[j]][i],g[j][0]=dis[l[j]][i]+1ll*p[j],
				mx[j][0]=s[f[j][0]]-g[j][0],tmp.pb(j);
		int siz=tmp.size();
		for(int k=1;k<24;k++)
			for(int j=0;j<siz;j++){
				int u=tmp[j];f[u][k]=f[f[u][k-1]][k-1];
				g[u][k]=g[u][k-1]+g[f[u][k-1]][k-1];
				mx[u][k]=min(mx[u][k-1],mx[f[u][k-1]][k-1]-g[u][k-1]);
			}
	}
}
LL simulate(int _x,int _z){
	int x=_x;LL z=_z;
	for(int j=0;j<24;j++){
		if(s[x]>=(1<<j)&&s[x]<(1<<j+1)){if(z>=s[x])z+=s[x],x=w[x];else z+=p[x],x=l[x];}
		if(z+dis[x][j]>=(1<<j+1))continue;
		z+=dis[x][j];x=nxt[x][j];
		if(z>=s[x]){z+=s[x];x=w[x];continue;}
		for(int k=23;k>=0;k--)
			if(z<mx[x][k]&&z+g[x][k]<(1<<j+1)&&f[x][k]!=n)
				z+=g[x][k],x=f[x][k];
		if(z+g[x][0]>=(1<<j+1)){if(z>=s[x])z+=s[x],x=w[x];else z+=p[x],x=l[x];continue;}
		z+=g[x][0];x=f[x][0];if(x==n)break;
	}
	z+=dp[x];
	return z;
}


这篇关于[IOI2021]地牢游戏的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程