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
分析
设f[i]表示以i为最后一位,能求出的序列最长为多少。
f[i]=max{f[j]+1} 一个可以转移的j要满足以下条件:
1. j< i
2. a[j]< a[i] (两个都不变化)
3. a[j]< Min[i] (i变化,Min[i]为i可变化的最小值)
4. Max[j]< a[i] (j变化,Max[j]为j可变化的最大值)
时间复杂度O(n2)
正解1
其实可以把条件2、3、4合并一下,然后变成max(a[j],Max[j])< a[i] 和a[j]< min(a[i],Min[i])两个条件
两个条件,很容易让人想到用树套树做。
时间复杂度O(nlog2n)
正解2
也是上面的两个条件。
枚举更新f[i]时,可以把它拆成两个操作:查询最大值和插入操作。
操作是相互独立的,而且可以离线做,所以可以考虑cdq分治
对于操作区间[l,r],先处理完[l,mid],然后用这部分的答案更新[mid+1,r]的答案,最后递归处理[mid+1,r]。离线更新答案可以按一个条件排序,然后用树状数组查询。
时间复杂度O(nlog2n)
cdq分治的代码
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=200005,N=100000;
int n,m,tot,a[maxn],b[maxn],c[maxn],f[maxn],s[maxn];
struct data
{
int x,y,v;
}A[maxn],B[maxn];
bool cmp(data a,data b)
{
return a.x<b.x;
}
int lowbit(int x)
{
return x & (-x);
}
void insert(int x,int y)
{
for (;x<=N;x+=lowbit(x)) s[x]=max(s[x],y);
}
int get(int x)
{
int k=0;
for (;x;x-=lowbit(x)) k=max(k,s[x]);
return k;
}
void clear(int x)
{
for (;x<=N;x+=lowbit(x)) s[x]=0;
}
void solve(int l,int r)
{
if (l==r)
{
f[l]=max(f[l],1);
return;
}
int mid=(l+r)/2,i,j;
solve(l,mid);
for (i=l;i<=mid;i++)
{
A[i].v=f[i];
A[i].x=a[i];
A[i].y=c[i];
}
for (i=mid+1;i<=r;i++)
{
B[i].v=i;
B[i].x=b[i];
B[i].y=a[i];
}
sort(A+l,A+mid+1,cmp);
sort(B+mid+1,B+r+1,cmp);
for (i=l,j=mid+1;j<=r;j++)
{
for (;i<=mid && A[i].x<=B[j].x;i++) insert(A[i].y,A[i].v);
f[B[j].v]=max(f[B[j].v],get(B[j].y)+1);
}
for (i--;i>=l;i--) clear(A[i].y);
solve(mid+1,r);
}
int main()
{
scanf("%d%d",&n,&m);
memset(b,42,sizeof(b));
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
while (m--)
{
int x,y;
scanf("%d%d",&x,&y);
b[x]=min(b[x],y);
c[x]=max(c[x],y);
}
for (int i=1;i<=n;i++)
{
b[i]=min(b[i],a[i]);
c[i]=max(c[i],a[i]);
}
solve(1,n);
int ans=0;
for (int i=1;i<=n;i++) ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}