4553: [Tjoi2016&Heoi2016]序列
Time Limit: 20 Sec Memory Limit: 128 MB
Submit: 260 Solved: 133
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
动态规划+CDQ分治
每种变化只有一个只发生变化,所以每个位置i只有最大值mx[i]和最小值mn[i]有用。
DP方程:f[i]=max{f[j]}+1,其中j<i,a[j]≤mn[i]且mx[j]≤a[i]。
用CDQ分治优化DP。最初的时候所有编号是递增的,然后每次计算左边对右边影响的时候,让左面的a值有序,右面的mn值有序,这样就满足了前两个条件,第三个条件mx[j]≤a[i]用一个树状数组维护。每次计算完之后按照a值归并排序。
注意,递归到每一层的顺序是:处理左边→计算左边对右边的影响→处理右边。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define N 100005 using namespace std; int n,m,ans,f[N],s[N]; struct data{int v,mn,mx,id;}a[N],t[N]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline bool cmpmn(data a,data b){return a.mn<b.mn;} inline bool cmpid(data a,data b){return a.id<b.id;} inline int query(int x) { int ret=0; for(;x;x-=(x&(-x))) ret=max(ret,s[x]); return ret; } inline void change(int x,int y){for(;x<=100000;x+=(x&(-x))) s[x]=max(s[x],y);} inline void clear(int x){for(;x<=100000;x+=(x&(-x))) s[x]=0;} void solve(int l,int r) { if (l>=r) return; int mid=(l+r)>>1; solve(l,mid); sort(a+mid+1,a+r+1,cmpmn); int tmp=l; F(i,mid+1,r) { while (tmp<=mid&&a[tmp].v<=a[i].mn) change(a[tmp].mx,f[a[tmp].id]),tmp++; f[a[i].id]=max(f[a[i].id],query(a[i].v)+1); } F(i,l,tmp-1) clear(a[i].mx); sort(a+mid+1,a+r+1,cmpid); solve(mid+1,r); int l1=l,l2=mid+1;tmp=l; while (l1<=mid&&l2<=r) t[tmp++]=a[l1].v<a[l2].v?a[l1++]:a[l2++]; while (l1<=mid) t[tmp++]=a[l1++]; while (l2<=r) t[tmp++]=a[l2++]; F(i,l,r) a[i]=t[i]; } int main() { n=read();m=read(); F(i,1,n) a[i].v=a[i].mn=a[i].mx=read(),a[i].id=i,f[i]=1; F(i,1,m){int x=read(),y=read();a[x].mn=min(a[x].mn,y);a[x].mx=max(a[x].mx,y);} solve(1,n); F(i,1,n) ans=max(ans,f[i]); printf("%d\n",ans); return 0; }