[CF1476F]Lanterns

题目

传送门 to CF

思路

首先这是一个 d p \tt dp dp 问题。那么我们需要找到递归子问题。

一般会枚举某个特值的情况。比如最后一个灯笼。考虑它覆盖了哪些灯笼?那未被照亮的灯笼是离散的,不行。那么可以考虑它被谁覆盖。

不妨设灯笼 j j j 覆盖了它。那么 ( j , n ] (j,n] (j,n] 灯笼都是照亮左边灯笼,被照亮的灯笼继续往左照亮,直到某个 p i = 0 p_i=0 pi=0 停止(照不到左侧)。此时只剩下 [ 1 , i ) [1,i) [1,i) 的灯笼没照亮,看看它们是否可以内部自行照亮即可。

这样是 O ( n 2 ) \mathcal O(n^2) O(n2) 的。注意到 d p \tt dp dp 值都是 b o o l \tt bool bool 类型,可以考虑将其改为 i n t \tt int int 类型,以此减小转移时所需处理的信息——显然就是用 i n t \tt int int 动态做到 “被照亮的灯笼继续往左照亮” 的过程,只需要存储被照亮的灯笼的数量即可。

当然这样也不用递归了,直接递推。完整地说一遍,记 f ( i ) f(i) f(i) 为前 i i i 个灯笼能照亮的最长前缀长度。转移则有两种,分别对应原来的枚举 j j j 和继续往后迭代照亮。

  • 任取 j ⩾ i j\geqslant i ji 满足 j − p j ⩽ f ( i − 1 ) + 1 j-p_j\leqslant f(i{\rm-}1)+1 jpjf(i1)+1,则 f ( j ) f(j) f(j) 可用 max ⁡ t = i j − 1 t + p t \max_{t=i}^{j-1}t+p_t maxt=ij1t+pt 更新。特别地,若 j = i j=i j=i,用 ( j − 1 ) (j{\rm-}1) (j1) 更新。
  • f ( i − 1 ) ⩾ i f(i{\rm-}1)\geqslant i f(i1)i,则 f ( i ) f(i) f(i) 可用 max ⁡ { f ( i − 1 ) , i + p i } \max\{f(i{\rm-}1),i+p_i\} max{f(i1),i+pi} 更新。

最有趣的是,第一类转移并不用到 f ( i − 1 ) f(i{\rm-}1) f(i1) 来更新,因为它本来就是在无法继续照亮的时候,才会需要重新枚举 j j j 。从式子上来理解,就是只有 f ( i − 1 ) < i f(i{\rm-}1)<i f(i1)<i 时需要考虑第一类转移,此时 t = i t=i t=i 就已经让 max ⁡ \max max 取更大值了。

此时换用填表法。对于一个 j j j,其最优的 i i i 肯定越小越好;所以目标只是找到最小的 i i i 满足 f ( i − 1 ) ⩾ j − p j − 1 f(i{\rm-}1)\geqslant j-p_j-1 f(i1)jpj1 。单调栈上二分即可实现。查询最大值则可 S T \tt ST ST 表,或者任何数据结构。时间复杂度 O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn)

代码

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG long for LONELINESS)
#include <cctype> // ZXY yydSISTER!!!
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void getMax(int &x, const int &y){
	if(x < y) x = y;
}

const int MAXN = 300005;
int p[MAXN];
namespace ST{
	const int LOGN = 19;
	int st[LOGN][MAXN], logtwo[MAXN];
	void init(int n){
		rep(i,2^(logtwo[1]=0),n) logtwo[i] = logtwo[i>>1]+1;
	}
	void build(int n){
		rep(i,1,n) st[0][i] = i+p[i];
		rep(j,0,LOGN-2) rep(i,1,n-(2<<j)+1)
			st[j+1][i] = max(st[j][i],st[j][i+(1<<j)]);
	}
	inline int query(const int &l, const int &r){
		const int &k = logtwo[r-l+1];
		return max(st[k][l],st[k][r-(1<<k)+1]);
	}
}

int best[MAXN], dp[MAXN], sta[MAXN], top;
void paint(int x){
	if(!x) return ;
	if(best[x] == -1){
		paint(x-1), putchar('R');
		return ; // covered
	}
	paint(best[x]);
	rep(i,best[x]+1,x-1) putchar('R');
	putchar('L'); // x is chosen to be left
}

bool cmp(const int &x, const int &y){
	return dp[x] < y;
}
int main(){
	ST::init(MAXN-1);
	for(int T=readint(); T; --T){
		int n = readint();
		rep(i,1,n) p[i] = readint();
		ST::build(n), sta[0] = top = 0;
		for(int i=1; i<=n; ++i){
			sta[top+1] = i; // force best[i] = i
			dp[i] = (dp[i-1] >= i) ? max(dp[i-1],i+p[i]) : 0;
			best[i] = *lower_bound(sta,sta+top+1,i-p[i]-1,cmp);
			int got = (best[i] == i) ? -1 : ((best[i] == i-1) ?
				i-1 : ST::query(best[i]+1,i-1)); // at least j-1
			if(dp[i] < got) dp[i] = got; else best[i] = -1;
			if(dp[sta[top]] < dp[i]) sta[++ top] = i;
		}
		if(dp[n] < n) puts("NO");
		else puts("YES"), paint(n), putchar('\n');
	}
	return 0;
}
以下是一个简单的灯笼高高挂小游戏的 Phaser 3 代码示例: ```javascript var config = { type: Phaser.AUTO, width: 800, height: 600, physics: { default: 'arcade', arcade: { gravity: { y: 200 } } }, scene: { preload: preload, create: create, update: update } }; var game = new Phaser.Game(config); var player; var lanterns; var cursors; var score = 0; var scoreText; function preload() { this.load.image('sky', 'assets/sky.png'); this.load.image('ground', 'assets/platform.png'); this.load.image('star', 'assets/star.png'); this.load.image('lantern', 'assets/lantern.png'); this.load.spritesheet('dude', 'assets/dude.png', { frameWidth: 32, frameHeight: 48 }); } function create() { this.add.image(400, 300, 'sky'); var platforms = this.physics.add.staticGroup(); platforms.create(400, 568, 'ground').setScale(2).refreshBody(); platforms.create(600, 400, 'ground'); platforms.create(50, 250, 'ground'); platforms.create(750, 220, 'ground'); player = this.physics.add.sprite(100, 450, 'dude'); player.setBounce(0.2); player.setCollideWorldBounds(true); this.anims.create({ key: 'left', frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }), frameRate: 10, repeat: -1 }); this.anims.create({ key: 'turn', frames: [ { key: 'dude', frame: 4 } ], frameRate: 20 }); this.anims.create({ key: 'right', frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }), frameRate: 10, repeat: -1 }); cursors = this.input.keyboard.createCursorKeys(); lanterns = this.physics.add.group({ key: 'lantern', repeat: 11, setXY: { x: 12, y: 0, stepX: 70 } }); lanterns.children.iterate(function (child) { child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8)); }); var stars = this.physics.add.group({ key: 'star', repeat: 11, setXY: { x: 12, y: 0, stepX: 70 } }); stars.children.iterate(function (child) { child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8)); }); this.physics.add.collider(player, platforms); this.physics.add.collider(lanterns, platforms); this.physics.add.collider(stars, platforms); this.physics.add.overlap(player, stars, collectStar, null, this); this.physics.add.overlap(player, lanterns, hitLantern, null, this); scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' }); } function update() { if (cursors.left.isDown) { player.setVelocityX(-160); player.anims.play('left', true); } else if (cursors.right.isDown) { player.setVelocityX(160); player.anims.play('right', true); } else { player.setVelocityX(0); player.anims.play('turn'); } if (cursors.up.isDown && player.body.touching.down) { player.setVelocityY(-330); } } function collectStar(player, star) { star.disableBody(true, true); score += 10; scoreText.setText('Score: ' + score); if (stars.countActive(true) === 0) { stars.children.iterate(function (child) { child.enableBody(true, child.x, 0, true, true); }); var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400); var lantern = lanterns.create(x, 16, 'lantern'); lantern.setBounce(1); lantern.setCollideWorldBounds(true); lantern.setVelocity(Phaser.Math.Between(-200, 200), 20); } } function hitLantern(player, lantern) { this.physics.pause(); player.setTint(0xff0000); player.anims.play('turn'); gameOver = true; } ``` 这段代码创建了一个包含玩家、星星和灯笼的简单平台游戏。玩家可以使用键盘控制移动和跳跃,收集星星并避开灯笼。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值