【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序列来表示。形如:
考虑一个合法的操作序列,对于一个连续的0/1的段,假设序列\([l,r]\)中均为0,\(c_{l-1}\neq 0\),则一定会有
由此可以知道,对于一段连续相同的操作序列,可以很简单判断是否合法。所以我们只要关心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\)则需要满足:
- 当\(x\in[i + 1,j]\), \(l_{1x}\leq k_x\leq r_{1x}\)
- 当\(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;
}

浙公网安备 33010602011771号