题意:
给你一个长度为 n 的数组,有 m 次操作:第 p 个数为 q,求现在这个数组的最长递增子序列长度(从第一个开始,算出答案后第 p 个数要回到原来的值)。
题解:
如果我们要修改第 p 个数,要考虑区间[1, p-1]里的最长递增子序列长度 d1[index](index 是其最大值的下表) 和最大值 num[index] , 如果 q 大于 num[index] 则要答案就要加一,且在区间[p+1, n]寻找第一个大于 q 的下标,以及这个点到 n 最长递增子序列长度 d2[index],就可以让他们相加就是答案了;同理如果 q 小于等于 num[index],则在区间[p+1, n]寻找第一个大于 num[index] 的下标,以及这个点到 n 最长递增子序列长度 d2[index](index 是更新后的下标),就可以让他们相加就是答案了。
例如:
[1,2,3,4,4] 对应的d1数组为 [1,2,3,4,4],d2数组为 [4,3,2,1,1]。
我们把第3个数改为 1,则答案为 d1[2] + d2[4] = 3。
有多种方法来找最大值的小标。
线段树版本:
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <bitset>
#include <stack>
#include <cmath>
#include <deque>
#include <queue>
#include <list>
#include <set>
#include <map>
#define mem(a) memset(a, 0, sizeof(a))
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn = 1e5+10;
struct Tree{
int l, r, maxx, index;
}tree[maxn << 2];
int ans, index, num[maxn], d1[maxn], d2[maxn];
void pushdown(int root){
if(tree[root << 1].maxx >=tree[root << 1 | 1].maxx){
tree[root].index = tree[root << 1].index;
tree[root].maxx = tree[root << 1].maxx;
}
else{
tree[root].index = tree[root << 1 | 1].index;
tree[root].maxx = tree[root << 1 | 1].maxx;
}
}
void build_tree(int root, int a, int b){
tree[root].l = a;
tree[root].r = b;
int l = tree[root].l;
int r = tree[root].r;
if(l == r){
tree[root].maxx = 0;
tree[root].index = 0;
return;
}
int mid = (a + b) >> 1;
build_tree(root << 1, a, mid);
build_tree(root << 1 | 1, mid+1, b);
pushdown(root);
}
void queryLeft(int root, int ql, int qr, int value){
int l = tree[root].l;
int r = tree[root].r;
if(l == r){
if(tree[root].maxx > value){
index = min(l, index);
}
return;
}
int mid = (l + r) >> 1;
if(l >= ql && qr >= r){
if(tree[root << 1].maxx > value){
queryLeft(root << 1, ql, qr, value);
}
else if(tree[root << 1 | 1].maxx > value){
queryLeft(root << 1 | 1, ql, qr, value);
}
return;
}
if(ql <= mid){
queryLeft(root << 1, ql, qr, value);
}
if(qr >= mid+1){
queryLeft(root << 1 | 1, ql, qr, value);
}
}
void update(int root, int id, int value){
int l = tree[root].l;
int r = tree[root].r;
if(l == r){
tree[root].maxx = value;
tree[root].index = l;
return;
}
int m = (l + r) >> 1;
if(id <= m){
update(root << 1, id, value);
}
else{
update(root << 1 | 1, id, value);
}
pushdown(root);
}
void queryRight(int root, int ql, int qr){
int l = tree[root].l;
int r = tree[root].r;
if(l >= ql && qr >= r){
if(tree[root].maxx > num[index]){
index = tree[root].index;
}
return;
}
int mid = (l + r) >> 1;
if(ql <= mid){
queryRight(root << 1, ql, qr);
}
if(qr >= mid+1){
queryRight(root << 1 | 1, ql, qr);
}
}
int main(){
int t;
scanf("%d", &t);
while(t--){
int n, m, maxx = 0;
scanf("%d %d", &n, &m);
mem(d1);
mem(d2);
build_tree(1, 1, n);
for(int i = 1; i <= n; i++){
scanf("%d", &num[i]);
d1[i] = d1[i-1];
if(num[i] > maxx){
d1[i]++;
maxx = num[i];
}
}
for(int i = n; i >= 1; i--){
index = n+1;
queryLeft(1, 1, n, num[i]);
if(index > n){
index = 0;
}
d2[i] = d2[index]+1;
update(1, i, num[i]);
}
// for(int i = 1; i <= n; i++){
// cout<<d1[i]<<" "<<d2[i]<<endl;
// }
while(m--){
ans = 0;
index = 0;
int a, b;
scanf("%d %d", &a, &b);
if(a != 1){
queryRight(1, 1, a-1);
}
ans += d1[index];
if(b > num[index]){
ans++;
}
else{
b = num[index];
}
index = n+1;
if(a != n){
queryLeft(1, a+1, n, b);
}
ans += d2[index];
printf("%d\n", ans);
}
}
}