[DP] codeforces 650D. Zip-line

题意:

给一个长度为n的序列 ai ,有m个询问,一次询问给出 pos,val ,问假如把 apos 变成 val ,序列的最长上升子序列的长度(设为 lis )是多少,注意询问不会改变原序列。

题解:

a_{pos} apos 变成 val 之后,通过思考可以发现单次询问时有两种情况,第一种是答案的 lis 中使用了 val ,第二种是没有使用。
首先考虑使用了,那么需要一些信息, lsa[i] 表示以 i 为结尾的最长lis的长度, lsb[i] 表示以 i 开头的最长lis的长度。
容易发现 lsalsb 都可以通过离线,在求原序列 lis 时求出来,正着求一次,倒着求一次就可以了,但是要注意询问里, i 位置的lsa[i]lsb[i]可以多有多个值,每个都要对应保存下来。
当处理某个询问时,使用了 val 的答案就是 lsa[i]+lsb[i]1
另外一种情况就是没有使用到 val ,就是判断 apos 在原序列的 lis 中是不是必须出现,如果可以被替代,那么答案就是 lis ,如果不可以被替代,答案是 lis1
判断 lis 中哪些元素一定出现,就是这个题
方法也是求出原序列的 lsalsb ,并且判断是否存在 ij lsa[i]==lsa[j]lsa[i]+lsb[i]==lis1lsa[j]+lsb[j]==lis1
如果存在则说明 i j可以互相替换。
这样两种情况就考虑完了。

#include<bits/stdc++.h>
using namespace std;
const int N = 4e5+5;
const int inf = ~0u>>2;
int a[N];
struct Q{
    int val, id;
    Q(){}
    Q(int b, int c){ val = b, id = c; }
};
vector<Q>qry[N];
int lsa[N], lsb[N];
int ansla[N], anslb[N];
int ans[N];
int p[N], pcnt;
int n, m;
int cs[N];
bool ok[N];
int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i) scanf("%d", a+i);
    for(int i = 1; i <= m; ++i){
        int pos, val;
        scanf("%d%d", &pos, &val);
        qry[pos].push_back(Q(val, i));
    }
    p[0] = -inf, pcnt = 1;
    for(int i = 1; i <= n; ++i){
        for(int k = qry[i].size()-1; k >= 0; --k){
            int pos = lower_bound(p, p+pcnt, qry[i][k].val)-p;
            ansla[qry[i][k].id] = pos;
        }
        int pos = lower_bound(p, p+pcnt, a[i])-p;
        lsa[i] = pos;
        p[pos] = a[i];
        if(pos == pcnt) ++pcnt;
    }
    int lis = pcnt-1;
    p[0] = -inf, pcnt = 1;
    for(int i = n; i >= 1; --i){
        for(int k = qry[i].size()-1; k >= 0; --k){
            int pos = lower_bound(p, p+pcnt, -qry[i][k].val)-p;
            anslb[qry[i][k].id] = pos;
        }
        int pos = lower_bound(p, p+pcnt, -a[i])-p;
        lsb[i] = pos;
        p[pos] = -a[i];
        if(pos == pcnt) ++pcnt;
    }
    memset(cs, -1, sizeof(cs));
    for(int i = 1; i <= n; ++i){
        if(lsa[i] + lsb[i] == lis+1){
            if(cs[lsa[i]] == -1) cs[lsa[i]] = i;
            else cs[lsa[i]] = -2;
        }
    }
    for(int i = 1; i <= lis; ++i) if(cs[i] > 0) ok[cs[i]] = 1;
    for(int i = 1; i <= n; ++i){
        for(int k = 0; k < qry[i].size(); ++k){
            ans[qry[i][k].id] = max(ok[i]?lis-1:lis, ansla[qry[i][k].id]+anslb[qry[i][k].id]-1);
        }
    }
    for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值