Codeforces Round #727 (Div. 2) E. Game with Cards(巧妙dp的优化)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #727 (Div. 2) E. Game with Cards(巧妙dp的优化)相关的知识,希望对你有一定的参考价值。
非常显然有一个 O ( n 2 ) O(n^2) O(n2)的 d p dp dp,虽然它的复杂度高的吓人,但还是让我们小心翼翼的把它写出来
定义 f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1]表示前 i i i个操作后,第 i i i次何左手/右手交换,且另一只手上是第 j j j次操作时的数
该状态是否存在
这样就是傻瓜式转移
然而复杂度太劣,我们考虑 d p dp dp优化中常用的套路,让数组存储的值发挥作用
考虑 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示第 i i i次操作和左手/右手交换,且另一只手的数最多可以延续到第 f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]轮操作,期间不需要交换
于是我们需要预处理一个数组 n x t [ i ] [ 0 / 1 ] nxt[i][0/1] nxt[i][0/1]表示第 i i i次操作的数,在左手/右手的限制下最多保持到哪不需要交换
以 k i k_i ki和左手交换为例
Ⅰ.若 f [ i − 1 ] [ 0 ] > = i f[i-1][0]>=i f[i−1][0]>=i,也就是右手握着的那个数能延续下去
f [ i ] [ 0 ] = m a x ( f [ i ] [ 0 ] , f [ i − 1 ] [ 0 ] ) \\rm f[i][0]=max(f[i][0],f[i-1][0]) f[i][0]=max(f[i][0],f[i−1][0])
Ⅱ.若 n x t [ i − 1 ] [ 1 ] > = i nxt[i-1][1]>=i nxt[i−1][1]>=i,也就是右手握着的 k [ i − 1 ] k[i-1] k[i−1]能延续下去
f [ i ] [ 0 ] = m a x ( f [ i ] [ 0 ] , n x t [ i − 1 ] [ 1 ] ) \\rm f[i][0]=max(f[i][0],nxt[i-1][1]) f[i][0]=max(f[i][0],nxt[i−1][1])
右手同理,于是我们得到了一个 O ( n ) O(n) O(n)的转移方程
剩下问题就是处理 n x t [ i ] [ 0 / 1 ] nxt[i][0/1] nxt[i][0/1]数组
我们可以使用 s t st st表维护区间最值,然后对每个 k i k_i ki往右边二分得到最远的点
只需要满足, k i k_i ki小于等于区间右限制的最小值,且大于等于区间左限制的最大值即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int n,m,a[maxn][2],b[maxn][2],k[maxn];
int mi[2][maxn][21],mx[2][maxn][21],nxt[maxn][2];
int f[maxn][2],pre[maxn][2];
/*
f[i][0/1]表示
截至第i次,左手选择i,右手最远能延续到的位置
截至第i次,右手选择i,左手最远能延续到的位置
*/
void read()
{
cin >> n >> m;
for(int i=1;i<=n;i++)
cin >> k[i] >> a[i][0] >> b[i][0] >> a[i][1] >> b[i][1];
}
int getmi(int id,int l,int r)
{
int k = log2(r-l+1);
return min( mi[id][l][k],mi[id][r-(1<<k)+1][k] );
}
int getmx(int id,int l,int r)
{
int k = log2(r-l+1);
return max( mx[id][l][k],mx[id][r-(1<<k)+1][k] );
}
void init(int id)
{
for(int i=1;i<=n;i++)//mi表示最大值的最小值
mi[id][i][0] = b[i][id], mx[id][i][0] = a[i][id];
for(int i=1;i<=20;i++)
for(int j=0;j+(1<<i)-1<=n;j++)
{
int x = j+(1<<(i-1));
mi[id][j][i] = min( mi[id][j][i-1],mi[id][x][i-1] );
mx[id][j][i] = max( mx[id][j][i-1],mx[id][x][i-1] );
}
for(int i=0;i<=n;i++)
{
int l = i, r = n, ans = 0;
while( r>=l )
{
int mid = l+r>>1;
int flag1 = ( k[i]<=getmi(id,i,mid) );//不能超过右端点
int flag2 = ( k[i]>=getmx(id,i,mid) );//不能小于左端点
if( flag1&&flag2 ) l = mid+1, ans = mid;
else r = mid-1;
}
nxt[i][id] = ans;
}
}
void dfs(int x,int y)
{
if( x==0 ) return;
dfs( x-1,pre[x][y] );
cout << y << " ";
}
void DP()
{
memset( f,-1,sizeof f );
f[0][0] = nxt[0][1], f[0][1] = nxt[0][0];
for(int i=1;i<=n;i++)
for(int j=0;j<=1;j++)
{
if( k[i]>=a[i][j] && k[i]<=b[i][j] )
{
if( f[i-1][j]!=-1 && f[i-1][j]>=i )//右手能延续,当然可以更新
f[i][j] = f[i-1][j], pre[i][j] = j;
if( f[i-1][j^1]!=-1 && nxt[i-1][j^1]>=max(f[i][j],i) )
f[i][j] = nxt[i-1][j^1], pre[i][j] = j^1;
}
}
if( f[n][0]+f[n][1]==-2 ) puts("No");
else
{
puts("Yes");
if( f[n][0]!=-1 ) dfs(n,0);
else dfs(n,1);
}
}
int main()
{
read();
init(0); init(1);
DP();
}
以上是关于Codeforces Round #727 (Div. 2) E. Game with Cards(巧妙dp的优化)的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #727 div.2 A-F题解
Codeforces Round #727 (Div. 2) F. Strange Array(思维,线段树子段和)
Codeforces Round #727 (Div. 2) E. Game with Cards(巧妙dp的优化)
Codeforces Round #727 (Div. 2) E. Game with Cards(dp优化,从n^2到nlog到n)