Codeforces Round #345 (Div. 1) D. Zip-line LIS 离线 离散化 线段树

题目链接:http://codeforces.com/contest/650/problem/D
题意:
给你n个数,m个询问

每次单点修改,然后问你现在整个序列的lis长度。

修改完之后,要求修改回去。

解法:

询问离线。
我们维护四个东西,dp1[i]表示从1开始到第i个位置的最长上升子序列长度,dp2[i]表示从n开始到第i个位置的最长递减子序列长度。dp3[i]表示第i个询问的那个位置从1开始到第x(即询问的位置)个位置的最长上升子序列长度,dp4[i]表示递减。
假如询问是x,y那么
然后我们判断一下第x个位置是不是lis的关键位置,是的话,ans=lis。否则的话,ans=lis-1。关键位置就是这个位置是全局lis不可替代的一个数。
然后ans = max(ans,dp3[i]+dp4[i]-1)这个很显然……
然后就完了。

//CF 650D

#include <bits/stdc++.h>
using namespace std;
const int maxn = 400050;
int n, m, a[maxn], dp1[maxn], dp2[maxn], dp3[maxn], dp4[maxn];
///dp1[i]表示从1开始到第i个位置的最长上升子序列长度,dp2[i]表示从n开始到第i个位置的最长递减子序列长度。
///dp3[i]表示第i个询问的那个位置从1开始到第x(即询问的位置)个位置的最长上升子序列长度,dp4[i]表示递减。
struct Seg{
    struct node{
        int l, r, maxlen;
    }T[maxn*10];
    void pushup(int rt){
        T[rt].maxlen = max(T[rt*2].maxlen, T[rt*2+1].maxlen);
    }
    void build(int l, int r, int rt){
        T[rt].l = l, T[rt].r = r, T[rt].maxlen = 0;
        if(l == r) return;
        int mid = (l+r)/2;
        build(l, mid, rt*2);
        build(mid+1, r, rt*2+1);
    }
    void update(int pos, int val, int rt){
        if(T[rt].l == pos && pos == T[rt].r){
            T[rt].maxlen = val;
            return;
        }
        int mid = (T[rt].l + T[rt].r) / 2;
        if(pos <= mid) update(pos, val, rt*2);
        else update(pos, val, rt*2+1);
        pushup(rt);
    }
    int query(int L, int R, int rt){
        if(L <= T[rt].l && T[rt].r <= R){
            return T[rt].maxlen;
        }
        else{
            int mid = (T[rt].l + T[rt].r) / 2;
            int res = 0;
            if(L <= mid) res = max(res, query(L, R, rt*2));
            if(mid < R) res = max(res, query(L, R, rt*2+1));
            return res;
        }
    }
}L, R;
map <int, int> H;
vector <int> V;
struct node{
    int x, y;
}Q[maxn];
vector <pair<int, int>> q[maxn]; //<idx, num>
int cnt[maxn];

int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++){
        scanf("%d", &a[i]);
        V.push_back(a[i]);
    }
    for(int i = 1; i <= m; i++){
        scanf("%d%d", &Q[i].x, &Q[i].y);
        V.push_back(Q[i].y);
    }
    sort(V.begin(), V.end());
    V.erase(unique(V.begin(), V.end()), V.end());
    for(int i = 0; i < V.size(); i++) H[V[i]] = i+1;
    for(int i = 1; i <= n; i++) a[i] = H[a[i]];
    for(int i = 1; i <= m; i++){
        Q[i].y = H[Q[i].y];
        q[Q[i].x].push_back(make_pair(i, Q[i].y));
    }
    L.build(1, V.size()+5, 1);
    for(int i = 1; i <= n; i++){
        dp1[i] = L.query(1, a[i]-1, 1) + 1;
        for(int j = 0; j < q[i].size(); j++){
            int id = q[i][j].first;
            int x = q[i][j].second;
            dp3[id] = L.query(1, x-1, 1) + 1;
        }
        L.update(a[i], dp1[i], 1);
    }
    reverse(a+1, a+n+1);
    R.build(1, V.size()+5, 1);
    for(int i = 1; i <= n; i++){
        dp2[i] = R.query(a[i]+1, V.size(), 1) + 1;
        for(int j = 0; j < q[n-i+1].size(); j++){
            int id = q[n-i+1][j].first;
            int x = q[n-i+1][j].second;
            dp4[id] = R.query(x+1, V.size(), 1) + 1;
        }
        R.update(a[i], dp2[i], 1);
    }
    int LIS = 0;
    for(int i = 1; i <= n; i++){
        LIS = max(LIS, dp1[i]);
    }
    for(int i = 1; i <= n; i++){
        if(dp1[i] + dp2[n-i+1] == LIS + 1){
            cnt[dp1[i]]++;
        }
    }
    for(int i = 1; i <= m; i++){
        int ans = LIS;
        int x = Q[i].x;
        if(dp1[x] + dp2[n-x+1] == LIS+1 && cnt[dp1[x]] == 1) ans--;
        ans = max(ans, dp3[i] + dp4[i] - 1);
        printf("%d\n", ans);
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值