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
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; }