POI 2010 MOT-Monotonicity 2

POI 2010 MOT-Monotonicity 2

​ 题目链接:POI2010 MOT-Monotonicity 2


​ 考虑 dp,二维 dp 十分好想,但是二维 dp 无论怎么优化最快也只能达到 \(O(n\times k\times\log(n))\),那么二维 dp 的想法只能被舍弃,我们肯定立马想到不用二维,设 \(f_i\) 表示以 \(i\) 为结尾的,所能形成满足条件的最长子序列的长度。可是这么定义状态能不能满足最优子结构这个性质呢。

​ 如果满足最优子结构这个性质,就说明从最优解转移一定不比非最优解转移劣

​ 下面我们证明这个状态满足最优子结构

​ 我们采取反证法,假设现在我们正在求 \(f_i\)\(\forall j\in[1,i-1]\),我们假设 \(f_j=x\),和一个比 \(x\) 短,以 \(j\) 为结尾的序列结尾再添上一个 \(i\) 就构成了长度为 \(f_i\) 的序列。即 \(f_i\) 不是由 \(f_j\) 这个最优解添上一个 \(i\) 获得的,而是由一个长度为 \(y\) 的非最优解序列获得的。那么根据我们的假设,有 \(x>y\)。我们设 \(\text{long}\) 序列为长度为 \(x\) 的,以 \(j\) 为结尾的最优解序列,\(\text{short}\) 序列为长度为 \(y\) 的,以 \(j\) 为结尾的非最优解序列,那么假设就是 \(f_i\) 是由 \(i\) 继承 \(\text{short}\) 序列得到序列的长度 ,而且 \(f_i\) 从某个最优解转移来的答案劣于由 \(\text{short}\) 转移来的答案。

​ 那么根据假设我们有 \(f_i=y+1,x>y\)。同时还有以下推论:

  • 位置 \(x\) 与 位置 \(y\) 的符号不同。

    如果他们相同的话,由于两个位置上的数值同为 \(a_j\) 那么 \(\text{long}\) 序列可以替代 \(\text{short}\) 序列,因为如果 \(a_i\) 能接在 \(\text{short}\) 后面就一定能接在 \(\text{long}\) 后面,而 \(x+1>y+1\) 显然,使用 \(\text{long}\) 序列更优,所以如果我们要满足我们的假设,\(a_i,a_j\) 就只能满足 \(y\) 后的符号而不满足 \(x\) 后的符号。

  • \(a_i\)\(a_j\) 不相等。

    如果相等,我们继续分类讨论,若 \(x\) 后符号为 \(\text{=}\) 则选 \(\text{long}\) 序列转移。若 \(x\) 后符号不为 \(\text{=}\) 由于 \(a_i=a_j\) 那么我们可以用 \(a_i\) 代替 \(a_j\) 得到一个长度为 \(x\) 的序列,即通过 \(\text{long}\)\(j\) 的前一个转移过来,而 \(x\ge y+1\) 所以 \(a_i=a_j\) 时,从最优解转移一定不劣。

  • 位置 \(y\) 的位置不是 \(\text{=}\)

    根据上述两推论得到。

那么不满足上述推论的情况就可以从最优解转移,我们继续探讨满足上述三种推论的情况。

由于 \(x>y\) 所以在 \(\text{long}\) 序列里,我们可以找到一个节点 \(k\) 使得 \(f_k=y\)。这样我们又有结论:

  • \(a_i,a_k\) 不能满足 \(y\) 位置后符号的关系。

    如果满足,那么从 \(f_k\) 转移过来的答案不劣于 \(\text{short}\) 转移过来的答案,这与我们的假设不符。

  • 如果 \(x\) 位置的符号是 \(\text{<}\),那么 \(y\) 位置的符号只能是 \(\text{>}\)

    因为根据之前的推论 \(x,y\) 位置符号不同,且 \(y\) 位置的符号不能是 \(\text{=}\) 号。

    然后我们顺着这种情形推下去,得到 \(a_k<a_i<a_j\)\(a_k,a_i\) 不满足符号而 \(a_i,a_j\) 满足),又因为位置 \(y\) 的符号是 \(\text{>}\)。那么存在一个 \(a_l(k<l\le j)\)\(l\) 后的符号一定是 \(\text{<}\),如果没有一个 \(l\) 后的符号是 \(\text{<}\) 那么 \(a_k,a_j\) 的关系应该是 \(a_k\ge a_i\)。这与上述情况相违背。那么既然存在这样的 \(l\),那我们取第一次出现的 \(l\) 就能保证 \(l\) 前的符号都是 \(\text{>},\text{=}\),就有 \(a_k\ge a_l<a_j\)\(a_l<a_k<a_i<a_j\),又 \(l\) 后的符号为 \(\text{<}\) 那么 \(f_i\) 可以由 \(f_l\) 转移来,而 \(f_l\ge f_k+1=y+1\),所以 \(f_i\ge y+2>y+1\)。因此在这种情况中,\(f_i\) 从某个最优解转移过来反而更优。

  • 如果 \(x\) 位置的符号是 \(\text{=}\),那么 \(y\) 位置的符号只能是 \(\text{>},\text{<}\)

    先看 \(y\) 位置为 \(\text{>}\) 的情况。发现这种情况于上述情况相同(因为在上面那种情况的推导中 \(x\) 位置的符号没有出现)。

    再看 \(y\) 位置为 \(\text{<}\) 的情况(其实这里与上面那个情况刚好相反,将上述推导符号反一反就可以得到,这里有兴趣的读者可以看,因为过程雷同也可以跳过)

    \(a_k>a_i>a_j\)\(a_k,a_i\) 不满足而 \(a_i,a_j\) 满足),又 \(y\) 位置的符号是 \(\text{<}\) 那么一定存在 \(a_l(k<l\le j)\) 满足 \(l\) 位置后的符号为 \(\text{>}\),否则 \(a_k,a_j\) 的关系应该是 \(a_k\le a_j\) 这与上述情况不符。所以 \(l\) 一定存在,我们取第一次出现的 \(l\),就能满足 \(l\) 前的符号全是 \(\text{<},\text{=}\) 那么有 \(a_k\le a_l>a_i\),又 \(l\) 后的符号是 \(\text{>}\),那么 \(f_i\) 可以由 \(f_l\) 转移而来,而 \(f_l\ge f_k+1\ge y+1\),则 \(f_i\ge y+2>y+1\) ,那么这种情况下也是用最优解转移过来更优。

  • 如果 \(x\) 位置的符号是 \(\text{>}\) 那么 \(y\) 位置的符号只能是 \(\text{<}\) 这与上述情况一致(因为 \(x\) 位置的符号没有在推导出现,也就没有影响)

​ 综上所述,\(f_i\) 从之前的最优解转移过来的答案一定不劣,甚至更优。那么有一个显然的 \(O(n^2)\) 的 dp。转移式为:

\[dp_i=\left\{ \begin{aligned} &dp_j+1(temp[dp_j]='\text{<}',a_i<a_j)\\ &dp_j+1(temp[dp_j]='\text{>}',a_i>a_j)\\ &dp_j+1(temp[dp_j]='\text{=}',a_i=a_j)\\ \end{aligned} \right. \]

​ 然后取个极值就好了。那么优化也是显然的,我们开两个树状数组,分别记录前缀最大值和后缀最大值,然后用数组记录一下相等的权值的 \(dp_i\) 的最大值。这题的主要难点就是想到一维 dp 的可行性。

代码如下:

#include<bits/stdc++.h>
#define lowbit(i) (i&(-i))
using namespace std;
const int MAXN = 5e5+5;
const int MX = 1e6;
int n,k,a[MAXN],b[MAXN];
int t1[MX+5],t2[MX+5],dp[MAXN],val[MX+5];
void upd(int *t,int pos,int x){for(int i=pos;i<=MX;i+=lowbit(i))t[i]=max(t[i],x);}
int query(int *t,int pos){int ans=0;for(int i=pos;i;i-=lowbit(i))ans=max(ans,t[i]);return ans;}
stack <int> st;
int main()
{
	scanf("%d %d",&n,&k);
	for(int i=1;i<=n;++i)
		scanf("%d",&a[i]);
	for(int i=1;i<=k;++i)//1 = ,0 < , 2 >
	{
		char s[4];
		scanf("%s",s+1);
		if(s[1]=='<') b[i]=0;
		else if(s[1]=='=') b[i]=1;
		else b[i]=2;
	}
	for(int i=k+1;i<=n;++i)
		b[i]=b[i-k];
	int ans=0,res=0;
	for(int i=1;i<=n;++i)
	{
		dp[i]=max(val[a[i]],max(query(t1,a[i]-1),query(t2,MX-a[i])))+1;
		if(b[dp[i]]==0)upd(t1,a[i],dp[i]);
		if(b[dp[i]]==2)upd(t2,MX-a[i]+1,dp[i]);
		if(b[dp[i]]==1)val[a[i]]=max(dp[i],val[a[i]]);
	}
	int ind=0;
	for(int i=1;i<=n;++i)
		if(dp[i]>ans) ans=dp[i],res=a[i],ind=i;
	printf("%d\n",ans);--ans;st.push(res);
	for(int i=ind-1;i>=1;--i)//输出,从尾部往前遍历数组,利用栈的特性。
	{
		if(ans==0) break;
		if(b[ans]==1)
		{
			if(res==a[i]&&dp[i]==ans)
			{
				st.push(a[i]);
				--ans;
				res=a[i];
			}
		}
		else if(b[ans]==0)
		{
			if(res>a[i]&&dp[i]==ans)
			{
				st.push(a[i]);
				--ans;
				res=a[i];
			}
		}
		else if(b[ans]==2)
		{
			if(res<a[i]&&dp[i]==ans)
			{
				st.push(a[i]);
				--ans;
				res=a[i];
			}
		}
	}
	while(!st.empty())
	{
		printf("%d ",st.top());
		st.pop();
	}
	return 0;
}

总结:太折磨了,鬼知道我考试被这个苟证明卡了多久,最后还是看题解才会的。。。

posted @ 2021-08-09 20:04  夜空之星  阅读(42)  评论(0编辑  收藏  举报