您的位置:首页 > 其它

bzoj 4553: [Tjoi2016&Heoi2016]序列

2017-07-17 17:07 274 查看

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
,q
;
bool cmp(const data &a,const data &b){return a.a<b.a;}
int n,m,tr
,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;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: