BZOJ-4553:序列(cdq分治)

                                       4553: [Tjoi2016&Heoi2016]序列

                                                                Time Limit: 20 Sec  Memory Limit: 128 MB
                                                                        Submit: 1330  Solved: 618

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

思路:因为每次变换都是独立的,所以对于某2个数x和y来说,要使得x<y恒成立,即可以构成不降序列。

则有max(x)<=y且x<=min(y)。其中max(x)表示x的所有变换中的最大值,min同理。

那么这个问题就变成了求满足 i<j,且a[i]<=min(a[j]),max(a[i])<=a[j]的最长序列。

这样就是经典的三维偏序问题了。用cdq分治即可。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e5+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
struct lenka
{
    int val,l,r,id;
}a[MAX];
int cmp1(const lenka& p,const lenka& q){return p.val<q.val;}
int cmp2(const lenka& p,const lenka& q){return p.l<q.l;}
int cmp3(const lenka& p,const lenka& q){return p.id<q.id;}
int n;
int d[MAX],A[MAX];
void add(int x,int y){while(x<=n){A[x]=max(A[x],y);x+=x&(-x);}}
int ask(int x){int ans=0;while(x){ans=max(ans,A[x]);x-=x&(-x);}return ans;}
void init(int x){while(x<=n){A[x]=0;x+=x&(-x);}}
void cdq(int L,int R)
{
    if(L==R)return;
    int m=(L+R)/2;
    cdq(L,m);
    sort(a+L,a+m+1,cmp1);
    sort(a+m+1,a+R+1,cmp2);
    int j=L;
    for(int i=m+1;i<=R;i++)
    {
        while(j<=m&&a[j].val<=a[i].l)
        {
            add(a[j].r,d[a[j].id]);
            j++;
        }
        d[a[i].id]=max(d[a[i].id],ask(a[i].val)+1);
    }
    for(int i=L;i<j;i++)init(a[i].r);
    sort(a+m+1,a+R+1,cmp3);
    cdq(m+1,R);
}
int main()
{
    int m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].val);
        a[i].l=a[i].r=a[i].val;
        a[i].id=i;
        d[i]=1;
    }
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        a[x].l=min(a[x].l,y);
        a[x].r=max(a[x].r,y);
    }
    cdq(1,n);
    printf("%d\n",*max_element(d+1,d+n+1));
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值