【09.11】Codeforces Round #727 (Div. 2) E题 Game with Cards (动态规划)

传送门

题意

有两个数\(a_0,a_1\),初始值均为0,有n次操作,每次操作你必须选择一个数\(a_i(i\in\{0,1\})\),并将\(a_i\)的值变为给定的\(k_i\),每次操作后有一组限制\([l_{0i},r_{0i}],[l_{1i},r_{1i}]\),每次操作后需要满足限制\(l_{0i}\leq a_0\leq r_{0i},l_{1i}\leq a_1\leq r_{1i}\)。问是否有一种操作方案,使得每次操作后都能满足限制条件,如果有,输出一种方案。

\(1\leq n \leq 10^5,0\leq k_i\leq 10^9\)

题解

如果选择\(a_1\),我们记为0,选择\(a_2\),我们记为1。于是一组操作序列可以用一个01序列来表示。形如:

\[\color{red}01111\color{blue}10000\color{red}01 \]

考虑一个合法的操作序列,对于一个连续的0/1的段,假设序列\([l,r]\)中均为0,\(c_{l-1}\neq 0\),则一定会有

\[l_{0i}\leq k_i\leq r_{0i} \]

\[l_{1i}\leq k_{l-1}\leq r_{1i} \]

由此可以知道,对于一段连续相同的操作序列,可以很简单判断是否合法。所以我们只要关心0与1的分界处,我把它称之为交换点。

交换点有两类,第一类为01(上图中的红色),第二类为10(上图中的蓝色)。
设第一类交换点为\(dp_0[i]\),表示第i个操作为0,第i+1个操作为1,设第一类交换点为\(dp_1[i]\),表示第i个操作为1,第i+1个操作为0。若\(dp\)值为1,表示第i位与第i+1位这么填,后缀操作序列可以合法,0表示不能合法。
考虑\(dp_0[i]\)的转移,设\(dp_1[j]=1\),若\(dp_0[i]=1\)则需要满足:

  1. \(x\in[i + 1,j]\)\(l_{1x}\leq k_x\leq r_{1x}\)
  2. \(x\in[i, j]\)\(l_{0x}\leq k_i\leq r_{0x}\)

可以发现当j越大时,转移条件越苛刻,所以只要找到一个最小的j满足\(dp_1[j]=1\),即可进行转移。时间复杂度可以控制到\(O(n)\)

最后还要注意边界条件,对于\(dp[n]\)来说,可以默认第n+1操作一定合法。对于\(dp[0]\)来说,可以假设\(k_0=l_0=r_0=0\)并完成转移。

代码

/*************************************************************************
	> File Name: 1.cpp
	> Author: Knowledge_llz
	> Mail: 925538513@qq.com 
	> Blog: https://www.cnblogs.com/Knowledge-Pig/ 
	> Created Time: 2021/9/10 9:47:59
 ************************************************************************/

#include<bits/stdc++.h>
#define For(i,a,b) for(int i=(a);i<=(b);++i)
#define LL long long
#define pb push_back
#define fi first
#define se second
#define pr pair<int,int>
#define mk(a,b) make_pair(a,b)
#define endl '\n'
using namespace std;
const int maxx = 2e5 + 10;
int n, m, c[maxx], dp[2][maxx], pos[2], l[2], r[2], nxt[2];
pr a[maxx], b[maxx];
bool in(int x, pr A){
	return x >= A.fi && x <= A.se;
}
void dfs(int id, int t){
	if(id > n) return;
	cout << t << " " ;
	if(dp[t][id]) dfs(id + 1, t ^ 1);
	else dfs(id + 1, t);
}
int main(){
	ios::sync_with_stdio(false); cin.tie(0);
#ifndef ONLINE_JUDGE
	freopen("input.in", "r", stdin);
	freopen("output.out", "w", stdout);
#endif
	cin >> n >> m;
	for(int i = 1; i <= n; ++i)
		cin >> c[i] >> a[i].fi >> a[i].se >> b[i].fi >> b[i].se;
	
	pos[0] = pos[1] = maxx - 10; nxt[0] = nxt[1] = maxx;
	l[0] = l[1] = -m; r[0] = r[1] = m;
	for(int i = n; i >= 0; --i){
		if(nxt[1] > pos[1] && l[0] <= c[i] && r[0] >= c[i]) dp[0][i] = in(c[i], a[i]);
		if(nxt[0] > pos[0] && l[1] <= c[i] && r[1] >= c[i]) dp[1][i] = in(c[i], b[i]);
		if(!in(c[i], a[i])) nxt[0] = i;
		if(!in(c[i], b[i])) nxt[1] = i;
		if(dp[0][i]) l[1] = -m, r[1] = m, pos[0] = i;
		if(dp[1][i]) l[0] = -m, r[0] = m, pos[1] = i;
		l[0] = max(l[0], a[i].fi);
		r[0] = min(r[0], a[i].se);
		l[1] = max(l[1], b[i].fi);
		r[1] = min(r[1], b[i].se);
	}
	if(dp[0][0]){ cout << "Yes\n" ; dfs(1, 1); }
	else if(dp[1][0]){ cout << "Yes\n" ; dfs(1, 0); }
	else{ cout << "No" << endl; }
	return 0;
}
posted @ 2021-09-11 13:08  Knowledge-Pig  阅读(15)  评论(0)    收藏  举报