[Poi2010]Monotonicity 2

问题 B: [Poi2010]Monotonicity 2

时间限制: 1 Sec   内存限制: 256 MB
提交: 58   解决: 41
[ 提交][ 状态][ 讨论版]

题目描述

给出N个正整数a[1..N],再给出K个关系符号(>、<或=)s[1..k]。
选出一个长度为L的子序列(不要求连续),要求这个子序列的第i项和第i+1项的的大小关系为s[(i-1)mod K+1]。
求出L的最大值。

输入

第一行两个正整数,分别表示N和K (N, K <= 500,000)。
第二行给出N个正整数,第i个正整数表示a[i] (a[i] <= 10^6)。
第三行给出K个空格隔开关系符号(>、<或=),第i个表示s[i]。

输出

一个正整数,表示L的最大值。

样例输入

7 3
2 4 3 1 3 5 3
< > =

样例输出

6


提示






选出的子序列为2 4 3 3 5 3,相邻大小关系分别是< > = < >。



Monotonicity2:解析: 
一眼考虑DP,不过这个DP我的确刚开始认为这是没有什么正确性的,然后我就不会辣,但是大家都说这题就是DP.. 
并且好像自己一顿想要构造也没有构造出来不合法的数据? 
那我就只能回到自己刚开始的DP思路了。 
设F[i]表示到第i位拿出来的最长序列长度。 
所以下一个符号显然是确定的。 
然后等于号的话,我们直接开一个数组记录哪一个值上一次出现的可行位置即可。 
小于号,丢到线段树里面,线段树对权值开,记录一段权值区间最大的已出现的f值,查询直接拿最大就好了。 
大于号同理,再开一个线段树丢进去即可。 
F[i]=max(F[j]+1并且i,j满足F[j]对应的大小关系) 
复杂度,查询O(1),插入O(log106) 
最坏O(nlogn)吧 

这个dp乍看不科学,仔细一看更不科学,所以作为一个执着BOY,我决定要造数据卡死波兰人民,但是我造着造着就......证出来了.........

这个就是把 < > =分开讨论每次找到f[i] 即以i为结尾的最长长度,然后一顿转移,那么我们发现如果只是讨论 符号那么由于符号顺序影响 我们的得到的是一个有后效性的dp 就是说我们这个时候找到最优解可能会影响最优解的得到,那么这个dp的正确性是怎样的呢?

证明:

首先我们假设我们找到了一个不是全部从最优转移点转移来的最优序列那么:I.最后一个点一定是从最优转移点转移过来 II. 倒数第二个点,如果不是最优转移点的结果,那么他可以把最后一个点顶掉,并且把自己换成最优转移点     (由于我们一直没有改变最优解,所以我们重复I II 就得到了一个最后两位都是最优解的最优序列) III. 然后我们看中间的点,我们总可以得到一个其之后的点都是由最优转移点转移而来的,那么我们可以把他换成最优转移er,那么得到了一个比原来肿了一点的新序列(我们先留着后面的点),那么现在我们的一串点他们之间都有符号,且他们都满足符号,只是现在不满足那个 s数组的顺序,那么我们发现在这次被操作的那个点的左右一段区间的符号是一样的,那么我们只要在这一段区间内删除掉连续的一段大小为新增长度的序列就可以了,我们从中间一个一个的删,删掉就补到中间来这样连续操作就可以了, 如果 中间那个数两边存在 = 就可以随意删,如果两边符号相同即 < < 或  > > 那么中间的店一定可以删掉 如果是 a<b>c 或 a>b<c 那么删掉b再按照a c 大小删掉左右的某个符号就好了,如果是a c相等那么就任意留一个a或c,那么如果我们现在只能删一个怎么办?如果只剩一个的话那么这个东西就是这段区间里唯一的旮沓了,那么他左右两边的符号一定相等(小证明:我们由于采取两边向中间沦陷的策略所以我们剩的点一定是要删除的区间两个边缘点之一,那么我们考虑这个区间,他一定正好覆盖两个轮回中间的一个轮回,那么边缘之一一定和另一个边缘的左出头一样

我们重复以上操作可以把这个最优解转化为全部由最优转移点转移而来的最优解,因此要是没有一个不是全部从最优转移点转移来的最优序列那么我们的dp一定是对的如果有那么我们一定有全部由最优转移点转移而来的最优解,因此我们的dp一定可以找到最优解

这个故事告诉我们考场证dp是一个正确的选择.......


#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<map>
#include<deque>
#include<cstdlib>
#include<algorithm>
#define V 500005
#define LL long long
using namespace std;
int n,mod;
char s[V];
map< int , int >ma;
int id;
int a[V],f[V],sp[V];
int mx1[V*5],mx2[V*5],mx3[V*5];
inline void U1( int x, int zz, int rt, int l, int r)
{
    if (l==r)
    {
      mx1[rt]=max(zz,mx1[rt]);
      return ;             
    }   
    int mid=(l+r)/2;
    if (x<=mid)U1(x,zz,rt*2,l,mid);
    else U1(x,zz,rt*2+1,mid+1,r);
    mx1[rt]=max(mx1[rt*2],mx1[rt*2+1]);
}
inline void U2( int x, int zz, int rt, int l, int r)
{ //cout<<x<<"  "<<zz<<"  $$"<<rt<<" "<<l<<"  "<<r<<endl;
    if (l==r)
    {
          mx2[rt]=max(zz,mx2[rt]);
          return ;             
    }   
    int mid=(l+r)/2;
    if (x<=mid)U2(x,zz,rt*2,l,mid);
    else U2(x,zz,rt*2+1,mid+1,r);
    mx2[rt]=max(mx2[rt*2],mx2[rt*2+1]);
}
inline void U3( int x, int zz, int rt, int l, int r)
{ //cout<<x<<" "<<zz<<" %% "<<rt<<" "<<l<<"  "<<r<<endl;
    if (l==r)
    {
          mx3[rt]=max(zz,mx3[rt]);
          return ;             
    }   
    int mid=(l+r)/2;
    if (x<=mid)U3(x,zz,rt*2,l,mid);
    else U3(x,zz,rt*2+1,mid+1,r);
    mx3[rt]=max(mx3[rt*2],mx3[rt*2+1]);
}
inline int Q1( int L, int R, int rt, int l, int r)
{ //cout<<L<<"  "<<R<<" ^^ "<<rt<<" "<<l<<"  "<<r<<endl;
    if (L<=l&&r<=R)
    return mx1[rt];  
    int mid=(l+r)/2;
    int ans=0;
    if (L<=mid)ans=max(ans,Q1(L,R,rt*2,l,mid));
    if (mid<R)ans=max(ans,Q1(L,R,rt*2+1,mid+1,r));
    return ans;
}
inline int Q2( int L, int R, int rt, int l, int r)
{
    if (L<=l&&r<=R)
    return mx2[rt];
    int mid=(l+r)/2;
    int ans=0;
    if (L<=mid)ans=max(ans,Q2(L,R,rt*2,l,mid));
    if (mid<R)ans=max(ans,Q2(L,R,rt*2+1,mid+1,r));
    return ans;
}
inline int Q3( int L, int R, int rt, int l, int r)
{
    if (L<=l&&r<=R)
    return mx3[rt];
    int mid=(l+r)/2;
    int ans=0;
    if (L<=mid)ans=max(ans,Q3(L,R,rt*2,l,mid));
    if (mid<R)ans=max(ans,Q3(L,R,rt*2+1,mid+1,r));
    return ans;
}
 
inline int haha()
{
     //freopen("in.txt","r",stdin);freopen("out.txt","w",stdout);
     //freopen("mayan.in","r",stdin); freopen("mayan.out","w",stdout);
     int k;
     cin>>n>>k;
     for ( int i=1;i<=n;i++)
     {
      cin>>a[i];
       sp[i]=a[i];     
     }
     sort(sp+1,sp+n+1);
     for ( int i=1;i<=n;i++)
     {
       if (!ma[sp[i]])
       ma[sp[i]]=++id;
     }
     for ( int i=1;i<=n;i++)
     a[i]=ma[a[i]];
     for ( int j=1;j<=n;j++)
     cin>>s[j];
     int ll,s1,s2,s3;
    
     for ( int i=1;i<=n;i++)
     {   
       s1=s2=s3=0;
        if (a[i]>1)
       s1=Q1(1,a[i]-1,1,1,id);
       s2=Q2(a[i]+1,id,1,1,id);
       if (a[i]<=id)
       s3=Q3(a[i],a[i],1,1,id);
       ll=max(s1,max(s2,s3));
       if (s[ll%k+1]== '<' )
       U1(a[i],ll+1,1,1,id);
       else if (s[ll%k+1]== '>' )
       U2(a[i],ll+1,1,1,id);
       else if (s[ll%k+1]== '=' )
       U3(a[i],ll+1,1,1,id);
     }
     int ans=max(Q1(1,id,1,1,id),max(Q2(1,id,1,1,id),Q3(1,id,1,1,id)));
     cout<<ans; //<<"  "<<Q1(1,id,1,1,id)<<"  "<<Q2(1,id,1,1,id)<<"  "<<Q3(1,id,1,1,id)<<endl;
     return 0;
}
int gg=haha();
int main()
{;}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值