P8277 [USACO22OPEN] Up Down Subsequence P 题解

P8277 USACO22OPEN Up Down Subsequence P 题解

题目传送门。

给一种码量有点大,但是思维难度不大的线段树优化 dp 做法。

一开始想了好久二分答案然后 check 的思路......

题意很简单,不说了。考虑 dp。

设 f i , 0 / 1 f_{i,0/1} fi,0/1 表示以 i i i 结尾,取出来后 对应的字符为 UD 的选取最大值。

转移:

更新 U:需要保证 j < i j<i j<i 并且 f j , 0 / 1 f_{j,0/1} fj,0/1 对应的字符是 U。因为我们正在更新 U

同理,更新 D 需要保证 j > i j>i j>i 且 f j , 0 / 1 f_{j,0/1} fj,0/1 对应的字符是 D

所以我们就得到了 O ( n 2 ) O(n^2) O(n2) 的部分分程序:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
const int N=3e5+5;
int n,f[N][2],a[N],ans;
string s;
int main(){
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	cin>>s;s=" "+s;
	for(int i=1;i<=n;++i)
	{
		int x=a[i];
		f[x][0]=f[x][1]=1;
		for(int j=1;j<i;++j)
		{
			int y=a[j];
			if(y<x)
			{
				if(s[f[y][0]]=='U')
					f[x][0]=max(f[x][0],f[y][0]+1);
				if(s[f[y][1]]=='U')
					f[x][0]=max(f[x][0],f[y][1]+1);
			}
			if(y>x)
			{
				if(s[f[y][0]]=='D')
					f[x][1]=max(f[x][1],f[y][0]+1);
				if(s[f[y][1]]=='D')
					f[x][1]=max(f[x][1],f[y][1]+1);
			}
		}
		ans=max(ans,max(f[x][0],f[x][1]));
	}
//	for(int i=1;i<=n;++i)
//	{
//		cout<<i<<": ";
//		cout<<f[i][0]<<' '<<f[i][1]<<'\n';
//	}
	cout<<ans-1<<'\n';
	return 0;
}

然后考虑优化。

注意到每次查询,要么是 1 , x − 1 1,x-1 1,x−1,要么是 x + 1 , n x+1,n x+1,n,是一段连续的区间。

所以可以考虑用线段树维护最大值。

具体地,为 UD 各开一个线段树。编号还是 0 0 0 对应 U, 1 1 1 对应 D

然后每次转移就只要 query 一下线段树就可以了,单次时间复杂度 O ( log ⁡ n ) O(\log n) O(logn)。

最后考虑更新线段树。

首先线段树编号对应 0 0 0 或 1 1 1。

然后就是把 f i , 0 / 1 f_{i,0/1} fi,0/1 插入线段树 i i i 号位。

注意有可能 f i , 0 f_{i,0} fi,0 与 f i , 1 f_{i,1} fi,1 都插进了 i i i 号位,所以线段树更新的时候要 node[p][i].maxn=max(val,node[p][i].maxn);,而不是 node[p][i].maxn=val;

总时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)。

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
const int N=3e5+5;
int lowbit(int x){return x&(-x);}
int n,f[N][2],a[N],ans,maxu,maxd;
string s;
struct NODE{
	int l,r,maxn;
}node[N*4][2];
#define lc (p<<1)
#define rc (p<<1|1)
void pushup(int p,int i)
{
	if(node[p][i].l==node[p][i].r)return;
	node[p][i].maxn=max(node[lc][i].maxn,node[rc][i].maxn);
	return;
}
void bld(int l,int r,int p,int i)
{
	node[p][i].l=l;node[p][i].r=r;
	if(l==r)return;
	int mid=(node[p][i].l+node[p][i].r)/2;
	bld(l,mid,lc,i);bld(mid+1,r,rc,i);
	pushup(p,i);
	return;
}
void addx(int x,int val,int p,int i)
{
//	cout<<node[p][i].l<<' '<<node[p][i].r<<'\n';
	if(node[p][i].l==node[p][i].r)
	{
		node[p][i].maxn=max(val,node[p][i].maxn);
		return; 
	}
	int mid=(node[p][i].l+node[p][i].r)/2;
	if(x<=mid)
		addx(x,val,lc,i);
	else
		addx(x,val,rc,i);
	pushup(p,i);
	return;
}
int query(int l,int r,int p,int i)
{
	if(l<=node[p][i].l&&node[p][i].r<=r)
		return node[p][i].maxn;
	int mid=(node[p][i].l+node[p][i].r)/2,ans=0;
	if(l<=mid)
		ans=max(ans,query(l,r,lc,i));
	if(mid<r)
		ans=max(ans,query(l,r,rc,i));
	return ans;
}
int main(){
	ios::sync_with_stdio(0);
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	cin>>s;s=" "+s;
	bld(1,n,1,0);
	bld(1,n,1,1);
	for(int i=1;i<=n;++i)
	{
		int x=a[i];
		f[x][0]=f[x][1]=1;
		f[x][0]=max(f[x][0],query(1,x-1,1,0)+1);
		f[x][1]=max(f[x][1],query(x+1,n,1,1)+1);
		ans=max(ans,max(f[x][0],f[x][1]));
		if(s[f[x][0]]=='U')
			addx(x,f[x][0],1,0);
		if(s[f[x][1]]=='U')
			addx(x,f[x][1],1,0);
		if(s[f[x][0]]=='D')
			addx(x,f[x][0],1,1);
		if(s[f[x][1]]=='D')
			addx(x,f[x][1],1,1);
	}
//	for(int i=1;i<=n;++i)
//	{
//		cout<<i<<": ";
//		cout<<f[i][0]<<' '<<f[i][1]<<'\n';
//	}
	cout<<ans-1<<'\n';
	return 0;
}

你说得对,但是本来想用树状数组的,但是发现好像不太容易用 1 , r 1,r 1,r 的答案减去 1 , l − 1 1,l-1 1,l−1 的答案,于是用了线段树。虽然常数大了点,但好歹思维难度不高。

UPD:写完后想了想,发现其实可以用树状数组。因为只有 x + 1 , n x+1,n x+1,n 这类型的区间,所以可以反着再建树状数组,反着查询。不过这太麻烦了。

相关推荐
PH = 713 小时前
动态规划-求最优解-自底向上
算法·动态规划
用户4978630507313 小时前
前缀和与差分
算法
weixin_4617694013 小时前
通过数组和队列构造二叉树方法(用于算法测试),C++ vector不能直接使用null
数据结构·c++·算法·vector·nullptr·null
caimouse13 小时前
Reactos 第 4 章 对象管理 — 4.1 对象与对象目录
服务器·c语言·开发语言·windows·架构
千寻girling13 小时前
一周没跑步了 ,今日跑步 5KM , 哑铃+健身 20min , 俯卧撑 30 个 ;
数据结构·c++·python·算法·leetcode·职场和发展·线性回归
坚果派·白晓明13 小时前
鸿蒙PC三方库使用:使用 AtomCode + Skills 自动完成鸿蒙化三方库spdlog集成
c++·华为·ai编程·harmonyos·skills·atomcode·c/c++三方库
玖玥拾13 小时前
C/C++ 基础笔记(九)联合、枚举及文件操作
c语言·c++·文件操作·枚举·联合
小糯米60113 小时前
C语言 动态内存管理
c语言·开发语言
liulilittle13 小时前
拥塞控制:公平性的不可能三角
网络·c++·网络协议·tcp/ip·计算机网络·tcp·通信
CQU_JIAKE13 小时前
6.5aaaaa
算法·深度优先