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的优化)相关的知识,希望对你有一定的参考价值。

LINK

非常显然有一个 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[i1][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[i1][0])

Ⅱ.若 n x t [ i − 1 ] [ 1 ] > = i nxt[i-1][1]>=i nxt[i1][1]>=i,也就是右手握着的 k [ i − 1 ] k[i-1] k[i1]能延续下去

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[i1][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)

Codeforces Round #436 E. Fire(背包dp+输出路径)

[ACM]Codeforces Round #534 (Div. 2)