【CF650D】 Zip-line

题目

题目描述
Vasya has decided to build a zip-line on trees of a nearby forest. He wants the line to be as long as possible but he doesn’t remember exactly the heights of all trees in the forest. He is sure that he remembers correct heights of all trees except, possibly, one of them.

It is known that the forest consists of nn trees staying in a row numbered from left to right with integers from 11 to nn . According to Vasya, the height of the ii -th tree is equal to h_{i}h
i

. The zip-line of length kk should hang over kk ( 1<=k<=n1<=k<=n ) trees i_{1},i_{2},…,i_{k}i
1

,i
2

,…,i
k

( i_{1}<i_{2}<…<i_{k} ) such that their heights form an increasing sequence, that is h_{i1}<h_{i2}<…<h_{ik} .

Petya had been in this forest together with Vasya, and he now has qq assumptions about the mistake in Vasya’s sequence hh . His ii -th assumption consists of two integers a_{i}a
i

and b_{i}b
i

indicating that, according to Petya, the height of the tree numbered a_{i}a
i

is actually equal to b_{i}b
i

. Note that Petya’s assumptions are independent from each other.

Your task is to find the maximum length of a zip-line that can be built over the trees under each of the qq assumptions.

In this problem the length of a zip line is considered equal to the number of trees that form this zip-line.

输入格式
The first line of the input contains two integers nn and mm ( 1<=n,m<=4000001<=n,m<=400000 ) — the number of the trees in the forest and the number of Petya’s assumptions, respectively.

The following line contains nn integers h_{i}h
i

( 1<=h_{i}<=10^{9}1<=h
i

<=10
9
) — the heights of trees according to Vasya.

Each of the following mm lines contains two integers a_{i}a
i

and b_{i}b
i

( 1<=a_{i}<=n1<=a
i

<=n , 1<=b_{i}<=10^{9}1<=b
i

<=10
9
).

输出格式
For each of the Petya’s assumptions output one integer, indicating the maximum length of a zip-line that can be built under this assumption.

题意翻译
$$

题目大意:
给定一个长度为nn的序列以及mm个操作,每个操作形如“a_i~b_ia
i

b
i

”,表示将序列中第a_ia
i

个数改为b_ib
i

对于每个操作,求出序列的最长严格上升子序列长度。

注意:每个操作之间彼此独立。(即每次操作未进行时的序列是输入时的原序列,而不是上一次操作后得到的序列)

输入格式:
第一行输入两个数nn和mm;

第二行输入nn个数,表示原序列;

接下来mm行,每行输入两个数a_i~b_ia
i

b
i

意义如上所述。

输出格式:
共mm行,每行一个数,为当前操作之后的最长严格上升子序列的长度。

输入输出样例
输入 #1复制
4 4
1 2 3 4
1 1
1 4
4 3
4 5
输出 #1复制
4
3
3
4
输入 #2复制
4 2
1 3 2 6
3 5
2 4
输出 #2复制
4
3
说明/提示
Consider the first sample. The first assumption actually coincides with the height remembered by Vasya. In the second assumption the heights of the trees are (4,2,3,4)(4,2,3,4) , in the third one they are (1,2,3,3)(1,2,3,3) and in the fourth one they are (1,2,3,5)(1,2,3,5) .

思路

考虑如果我修改了一个数字,那么这时候答案是两部分的最大值:

1、经过这个点的LIS。 2、不经过这个点的LIS。

我们先考虑做1。

对于经过每个点的,就是前缀比他小的和后缀比它大的加起来,再加上它。

我们再分成三部分。

对于前缀比它小的,我们从左往右扫描,同时统计相应的答案(用树状数组维护)后缀同理,再加一。当然也可以强行主席树做。

考虑2,不经过这点的LIS。

显然答案要么是原来的len,要么是len-1。

这样我们只需要判断是否每一个LIS都必须经过它。

首先需要判断这个点是否能成为LIS的一部分。

设这个点的位置是i,要满足这个条件必须满足以i结束的最长上升子序列的长度加上以i开始的最长上升子序列的长度是原来的LIS-1。

同时,你要保证它是唯一的。

比如对于1 2 2 3,中间的两个2就不是唯一的。

怎么判断是否是唯一的?就是只有它能够转移到最后面,也就是如果存在(i,j)满足都可能成为LIS的一部分的,如果以i结束的LIS的长度和以j结束的LIS长度相同,那么i和j就可以互相替换。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
namespace TREE{
    struct node{
        int l,r;
        int val;
    }tree[N<<3];
    void build(int x,int l,int r){
        tree[x].l=l,tree[x].r=r,tree[x].val=0;
        if(l==r)return;
        int mid=l+r>>1;
        build(x<<1,l,mid);
        build(x<<1|1,mid+1,r);
    }
    void change(int x,int pos,int val){
        tree[x].val=max(tree[x].val,val);
        if(tree[x].l==tree[x].r)
            return;
        int mid=tree[x].l+tree[x].r>>1;
        if(pos<=mid)change(x<<1,pos,val);
        else change(x<<1|1,pos,val);
    }
    int query(int x,int l,int r){
        if(l<=tree[x].l&&tree[x].r<=r)return tree[x].val;
        if(l>tree[x].r||r<tree[x].l)return 0;
        return max(query(x<<1,l,r),query(x<<1|1,l,r));
    }
}
using namespace TREE;
int n,m;
int a[N];
struct nnn{
    int  a,b;
    int ans;
}cmd[N];
vector<int>v[N];
set<int>S;map<int,int>M;
int tot=0;int pos[N];
int f[N],g[N];
signed main(){
    n=read();m=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),S.insert(a[i]);
    for(int i=1;i<=m;i++)
        cmd[i].a=read(),cmd[i].b=read(),S.insert(cmd[i].b),v[cmd[i].a].push_back(i);
    for(set<int>::iterator it=S.begin();it!=S.end();it++)
        M[*it]=++tot;
    build(1,1,tot);
    int len=0;
    for(int i=1;i<=n;i++){
        f[i]=query(1,1,M[a[i]]-1)+1;
        for(int j=0;j<v[i].size();j++)
            cmd[v[i][j]].ans+=query(1,1,M[cmd[v[i][j]].b]-1);
        change(1,M[a[i]],f[i]);
        len=max(len,f[i]);
        //write(f[i]),putchar('\n');
    }
    build(1,1,tot);
    for(int i=n;i>=1;i--){
        g[i]=query(1,M[a[i]]+1,tot)+1;
        for(int j=0;j<v[i].size();j++)
            cmd[v[i][j]].ans+=query(1,M[cmd[v[i][j]].b]+1,tot);
        change(1,M[a[i]],g[i]);
    }
    for(int i=1;i<=n;i++)
        if(f[i]+g[i]==len+1)
            pos[f[i]]++;
    for(int i=1;i<=m;i++){
        int A=cmd[i].a,B=cmd[i].b,C=cmd[i].ans;
        if(f[A]+g[A]==len+1&&pos[f[A]]==1)
            write(max(len-1,C+1)),putchar('\n');
        else
            write(max(len,C+1)),putchar('\n');
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值