bzoj 4553: [Tjoi2016&Heoi2016]序列

Description

 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他。玩具上有一个数列,数列中某些项的值

可能会变化,但同一个时刻最多只有一个值发生变化。现在佳媛姐姐已经研究出了所有变化的可能性,她想请教你
,能否选出一个子序列,使得在任意一种变化中,这个子序列都是不降的?请你告诉她这个子序列的最长长度即可
。注意:每种变化最多只有一个值发生变化。在样例输入1中,所有的变化是:
1 2 3
2 2 3
1 3 3
1 1 31 2 4
选择子序列为原序列,即在任意一种变化中均为不降子序列在样例输入2中,所有的变化是:3 3 33 2 3选择子序列
为第一个元素和第三个元素,或者第二个元素和第三个元素,均可满足要求

Input

 输入的第一行有两个正整数n, m,分别表示序列的长度和变化的个数。接下来一行有n个数,表示这个数列原始的

状态。接下来m行,每行有2个数x, y,表示数列的第x项可以变化成y这个值。1 <= x <= n。所有数字均为正整数
,且小于等于100,000

Output

 输出一个整数,表示对应的答案

Sample Input

3 4
1 2 3
1 2
2 3
2 1
3 4

Sample Output

3

HINT

Source

易得方程:

dp[i]=max{dp[j]+1}(j<i&&MAX[j]<=a[i]&&a[j]<=MIN[i])

然后我们发现这是一个三维偏序问题,可以使用CDQ+树状数组来进行优化。。。

设dp[i]表示以i号结尾的最长不下降子序列的长度,对于分治区间 𝑙, 𝑟 ,我们先递归处理编号上的左半部分
[𝑙, 𝑚𝑖𝑑],然后用左半部分更新右半部分。这样的更新方式,保证了左半部分更新右半部分时左半部分解的
最优性(因为右半部分不会对其迚行更新(即满足𝑖 ≥ 𝑗))。对于当前处理的区间,使用归并+树状数组进行更
新。

然而我一下就被lst大佬问得怀疑人生。。。仔细琢磨了一下,大致是这样的。。。

首先在外面把a排序。。。

然后solve(l,r)的操作:
1.把[l,r]按照序号分为两个部分(j<i这一维就解决了)

2.solve(l,mid),递归处理左边,处理完之后左边变为按照MAX增序,右边仍然是按照a排序。。。(这样MAX[j]<=a[i]这一维就解决来)

3.用左边区间[l,mid]的dp值来更新右边[mid+1,r]的dp值,具体做法就是把(a[j]<=MIN[i])这一维丢进树状数组,然后查询。。。

4.solve(mid+1,r),递归处理[mid+1,r];

5.按照MAX归并[l,r]。。。

感觉CDQ一直学得云里雾里放光彩。。。GG。。。这个题真的是细思极恐。。。我可能学了一个假的CDQ。。。

// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=100050;
struct data{
  int a,Min,Max,id,dp;
}g[N],q[N];
bool cmp(const data &a,const data &b){return a.a<b.a;}
int n,m,tr[N],T=1e5;
void update(int x,int val){
  for(int i=x;i<=T;i+=i&-i) tr[i]=max(tr[i],val);
}
void del(int x){
  for(int i=x;i<=T;i+=i&-i) tr[i]=0;
}
int query(int x){
  int ret=0;
  for(int i=x;i;i-=i&-i) ret=max(ret,tr[i]);
  return ret;
}
void CDQ(int l,int r){
  if(l==r){g[l].dp=max(g[l].dp,1);return;}
  int mid=(l+r)>>1;int tt=l-1,sum=mid;
  for(int i=l;i<=r;i++){
    if(g[i].id<=mid) q[++tt]=g[i];else q[++sum]=g[i];
  }
  for(int i=l;i<=r;i++) g[i]=q[i];CDQ(l,mid);
  for(int i=mid+1,x=l;i<=r;i++){
    for(;g[x].Max<=g[i].a&&x<=mid;x++) update(g[x].a,g[x].dp);
    g[i].dp=max(g[i].dp,query(g[i].Min)+1);
  }
  for(int i=l;i<=mid;i++) del(g[i].a);
  CDQ(mid+1,r);tt=l,sum=mid+1;
  for(int i=l;i<=r;i++){
    if((g[tt].Max<=g[sum].Max&&tt<=mid)||(g[tt].Max>g[sum].Max&&sum>r))
      q[i]=g[tt++];
    else q[i]=g[sum++]; 
  }
  for(int i=l;i<=r;i++) g[i]=q[i];
}
int main(){
  scanf("%d%d",&n,&m);int x,y,ans=0;
  for(int i=1;i<=n;i++) scanf("%d",&g[i].a),g[i].Min=g[i].a,g[i].Max=g[i].a,g[i].id=i;
  for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),g[x].Min=min(g[x].Min,y),g[x].Max=max(g[x].Max,y);
  sort(g+1,g+1+n,cmp);CDQ(1,n);for(int i=1;i<=n;i++) ans=max(ans,g[i].dp);
  printf("%d\n",ans);
  return 0;
}

  

posted @ 2017-07-17 17:07  qt666  阅读(174)  评论(0编辑  收藏  举报