[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

分析

设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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值