牛客练习赛25B——最长区间【线段树,思维,模拟】

链接:https://www.nowcoder.com/acm/contest/158/B
来源:牛客网
 

题目描述

给你一个长度为 n 的序列 a ,求最长的连续的严格上升区间的长度。
同时会进行 m 次修改,给定 x , y ,表示将 ax 修改为 y ,每次修改之后都要求输出答案。

输入描述:

第一行 2 个数 n,m,表示序列长度,修改次数; 
接下来一行 n 个数表示  ;
接下来 m 行,每行 2 个数 x , y ,描述一次修改。

输出描述:

第一行 1 个数表示最初的答案;
接下来 m 行,第 i 行 1 个数表示第 i 次修改后的答案。

示例1

输入

复制

4 3
1 2 3 4
3 1
2 5
3 7

输出

复制

4
2
2
3

说明

序列变换如下:
1  2  3  4
1  2  1  4
1  5  1  4
1  5  7  4

备注:

n,m ≤ 100000,1 ≤ x ≤ n,1 ≤ ai,y ≤ 100

这道题最开始的时候写的时候是直接暴力,结果超时了。然后看了题解,和大佬的代码才明白其中一种做法。这道题有两种做法。第一种:我们用一个数组b[i]表示从第i个数开始连续上升子序列的长度,c[i]表示长度为i的上升子序列的个数。我们每改变一个数字,就要判断一下a[x],从第x位开始对后面序列长度造成的影响,因为1<=a[i]<=100,上升序列的长度最长只有100位,我们只用判断x+100位后面上升数列的长度的变化。

第一种方法的代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5+10;
int cnt[MAXN],a[MAXN], b[MAXN];
 
int solve(){
    for(int i = 100; i >= 1; i--){
        if(cnt[i]){
            return i;
        }
    }
    return 0;
}
 
int main(){
    int n, m;
    while(~scanf("%d %d",&n, &m)){
        memset(cnt, 0, sizeof(cnt));
        for(int i = 1; i <= n; i++){
            scanf("%d",&a[i]);
            if(a[i] > a[i-1]) b[i] = b[i-1] + 1;
            else b[i] = 1;
            cnt[b[i]]++;
        }
        printf("%d\n",solve());
        while(m--){
            int x, y;
            scanf("%d %d",&x, &y);
            a[x] = y;
            for(int i = x; i <= min(x+100,n); i++){
                int s = 1;
                if(a[i] > a[i-1]) s = b[i-1] + 1;
                cnt[b[i]]--;
                b[i] = s;
                cnt[b[i]]++;
            }
            printf("%d\n",solve());
        }
    }
    return 0;
}

 第二种方法是用线段树做,不过这种方法我还没搞懂,先上一份模仿大佬的代码。在这里立一个flag,一周内把它给搞懂,然后将出来。

#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 2e5+110;
int a[MAXN];
struct SegementTree{
	int lm,rm;
	int sum;
	#define le(x) tree[x].lm
	#define ri(x) tree[x].rm
	#define sum(x) tree[x].sum
}tree[MAXN << 2];


void push_up(int u, int l, int r){
	le(u) = le(2*u);
	ri(u) = ri(2*u+1);
	sum(u) = max(sum(2*u), sum(2*u+1));
	int mid = (l+r) >> 1;
	if(a[mid] < a[mid+1]){
		if(le(2*u) == mid-l+1) le(u) = le(2*u) + le(2*u+1);
		if(ri(2*u+1) == r-mid) ri(u) = ri(2*u) + ri(2*u+1);
		int t = ri(2*u) + le(2*u+1);
		if(t > sum(u)) sum(u) = t;
	}
}

void build(int p,int l, int r){
	if(l == r){
		le(p) = ri(p) = sum(p) = 1;
		return ;
	}
	int mid = (l+r) >> 1;
	build(2*p,l, mid);
	build(2*p+1, mid+1, r);
	push_up(p,l,r);
}

void change(int u, int l, int r, int p, int d){
	if(l == r){
		a[l] = d;
		return ;
	}
	int mid = (l+r) >> 1;
	if(p <= mid)
		change(2*u,l,mid, p, d);
	else
		change(2*u+1,mid+1,r, p, d);
	push_up(u, l, r);
}

int ask(int u, int l, int r, int tl, int tr){
	if(tl <= l && r <= tr)
		return sum(u);
	int mid = (l+r) >> 1;
	if(tr <= mid)
		return ask(2*u,l,mid,tl,tr);
	else if(tl > mid)
		return ask(2*u+1,mid+1,r, tl, tr);
	else{
		int t1 = ask(2*u,l,mid,tl, mid), t2 = ask(2*u+1,mid+1,r, mid+1, tr), t = max(t1,t2);
		if(a[mid] < a[mid+1])
			t1 = min(ri(2*u), mid-tl+1), t2 = min(le(2*u+1),tr-mid),t1 += t2;
		return max(t,t1);
	}
}

int main(){
	int n, m;
	while(~scanf("%d %d",&n,&m)){
		for(int i = 1; i <= n; i++)
			scanf("%d",&a[i]);
		build(1, 1, n);
		printf("%d\n",ask(1,1,n,1,n));
		while(m--){
			int l,r;
			scanf("%d %d",&l, &r);
			change(1,1,n,l,r);
			printf("%d\n", ask(1,1,n,1,n));
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xiang_hehe/article/details/82053059
今日推荐