bzoj4553 [Tjoi2016&Heoi2016]序列

http://www.elijahqi.win/archives/2949
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分治
首先得会这道题n^2的dp 这怎么写 设dp[i]表示以i结尾的符合要求的最长连续子序列 那么可以知道我需要满足一个偏序关系才可以设定fr表示这个数在变化中最小是fr to表示这个数在变化中最大是to a表示这个数初始是多少
考虑如果dp[j]可以更新dp[i]那么一定满足toj<=ai aj<=fri j< i那么可以看出实际上我需要找一个前面这样的最大的dp值来更新i的答案 那么就可以cdq来做了 把i,j看作是时间的一维度 每次分治的时候对to&a进行排序 然后做的时候用树状数组位数a,fr这一维 然后正常cdq分治即可


#include<cstdio>
#include<cctype>
#include<cstring>
#define N 100010
#include<algorithm>
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if(T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(!isdigit(ch)) {if (ch=='-') f=-1;ch=gc();}
    while(isdigit(ch)) x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct node{
    int fr,a,to,id;
}data[N],tmp[N];
int mx,s[N],n,m,dp[N];
inline void add(int x,int v){while(x<=mx) s[x]=max(s[x],v),x+=x&-x;}
inline int query(int x){int tmp=0;while(x) tmp=max(tmp,s[x]),x-=x&-x;return tmp;}
inline void clear(int x){for (int i=x;i<=mx;i+=i&-i) if (!s[i]) return;else s[i]=0;}
inline bool cmpt(const node &a,const node &b){return a.to<b.to;}
inline bool cmpa(const node &a,const node &b){return a.a<b.a;}
inline bool cmpid(const node &a,const node &b){return a.id<b.id;}
inline void cdq(int l,int r){
    if (l==r) return;int mid=l+r>>1;int now1=l,now2=mid+1;
    for (int i=l;i<=r;++i)
        if (data[i].id<=mid) tmp[now1]=data[i],++now1;else tmp[now2]=data[i],++now2;
    memcpy(data+l,tmp+l,sizeof(node)*(r-l+1));cdq(l,mid);now1=l,now2=mid+1;
    sort(data+l,data+mid+1,cmpt);sort(data+mid+1,data+r+1,cmpa);
    while(now1<=mid&&now2<=r){
        int id1=data[now1].id,id2=data[now2].id;
        if (data[now1].to<=data[now2].a) add(data[now1].a,dp[id1]),++now1;else
        dp[id2]=max(dp[id2],query(data[now2].fr)+1),++now2;
    }int id2=0;
    while(now2<=r) id2=data[now2].id,dp[id2]=max(dp[id2],query(data[now2].fr)+1),++now2;
    for (int i=l;i<=r;++i) if (data[i].id<=mid) clear(data[i].a);
    cdq(mid+1,r);
}
int main(){
//  freopen("bzoj4553.in","r",stdin);
    n=read();m=read();
    for (int i=1;i<=n;++i) data[i].a=data[i].to=data[i].fr=read(),data[i].id=i,dp[i]=1,mx=max(mx,data[i].a);
    for (int i=1;i<=m;++i){static int x,y;
        x=read();y=read();data[x].fr=min(data[x].fr,y);data[x].to=max(data[x].to,y);
    }cdq(1,n);int ans=0;
    for (int i=1;i<=n;++i) ans=max(ans,dp[i]);
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值