HDU 3308 线段树之区间合并

点击打开链接

题意:T组数据,每组n和m,代表n个数和m次操作,U代表将第a个数的值改为b,Q代表询问a~b区间的最长连续上升子序列的长度,严格上升的

思路:一看到询问多少次了这种,肯定是线段树不用想,问区间的最长上升,可以用区间合并,lnum代表从区间左第一个元素开始的最长上升长度,注意第一个元素必须有,rnum代表从区间必须有最后一个元素的最长上升子序列,,mmnum代表区间最长的上升子序列,而num现在存的并不是树了,而是数据,具体的还有注释

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=100010;
int num[maxn],lnum[maxn*4],rnum[maxn*4],mmnum[maxn*4];
void pushup(int node,int le,int ri){
    lnum[node]=lnum[node<<1];
    rnum[node]=rnum[node<<1|1];
    mmnum[node]=max(mmnum[node<<1],mmnum[node<<1|1]);
    int t=(le+ri)>>1;
    int node1=(ri-le)+1;
    if(num[t]<num[t+1]){//此时我们的mmnum共有三种情况,左儿子右儿子和中间可以合并的部分的最大值,左右儿子上面已经比过了
        if(lnum[node]==node1-(node1>>1)) lnum[node]+=lnum[node<<1|1];//现在的lnum[node]=lnum[node<<1],所以如果这个区间整个区间都
        //是上升序列,那就加上左区间的右半部分的长度,下面的右区间是一回事
        if(rnum[node]==node1>>1) rnum[node]+=rnum[node<<1];
        mmnum[node]=max(mmnum[node],lnum[node<<1|1]+rnum[node<<1]);
    }
}
void buildtree(int le,int ri,int node){
    if(le==ri){
        lnum[node]=rnum[node]=mmnum[node]=1;
        return ;
    }
    int t=(le+ri)>>1;
    buildtree(le,t,node<<1);
    buildtree(t+1,ri,node<<1|1);
    pushup(node,le,ri);
}
void update(int pos,int le,int ri,int node){
    if(le==ri) return ;
    int t=(le+ri)>>1;
    if(pos<=t) update(pos,le,t,node<<1);
    else update(pos,t+1,ri,node<<1|1);
    pushup(node,le,ri);
}
int query(int l,int r,int le,int ri,int node){
    if(l<=le&&ri<=r) return mmnum[node];
    int t=(le+ri)>>1;
    if(r<=t) return query(l,r,le,t,node<<1);//区间必须完全的包括起来,才可以查询,不然和pushup一样三种情况找最大值
    if(l>t) return query(l,r,t+1,ri,node<<1|1);
    int ans1=query(l,r,le,t,node<<1);//左儿子
    int ans2=query(l,r,t+1,ri,node<<1|1);//右儿子
    int ans=max(ans1,ans2);//比较
    if(num[t]<num[t+1]){
        int k=min(lnum[node<<1|1],r-t)+min(rnum[node<<1],t-l+1);//可以合并的部分,根据lnum和rnum的定义画出来一目了然
        ans=max(ans,k);//最大值
    }
    return ans;
}
int main(){
    int T,n,m,a,b,c;
    char ch[2];
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",&num[i]);
        buildtree(1,n,1);
        while(m--){
            scanf("%s%d%d",ch,&a,&b);
            if(ch[0]=='U'){
                a++;num[a]=b;
                update(a,1,n,1);
            }else{
                a++;b++;
                int ans=query(a,b,1,n,1);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}
时间: 2024-07-30 12:29:26

HDU 3308 线段树之区间合并的相关文章

HDU 3308 线段树(区间合并)

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3896    Accepted Submission(s): 1766 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

hdu 3308 线段树单点更新 区间合并

http://acm.hdu.edu.cn/showproblem.php?pid=3308 学到两点: 1.以区间端点为开始/结束的最长......似乎在Dp也常用这种思想 2.分类的时候,明确标准逐层分类,思维格式: 条件一成立: { 条件二成立: { } else { } } else { 条件二成立: { } else { } } 上面的这种方式很清晰,如果直接想到那种情况iif(条件一 &条件二)就写,很容易出错而且把自己搞乱,或者情况不全,,,我就因为这WA了几次 3.WA了之后 ,

HDU 1540 &amp;&amp; POJ 2892 Tunnel Warfare (线段树,区间合并).

~~~~ 第一次遇到线段树合并的题,又被律爷教做人.TAT. ~~~~ 线段树的题意都很好理解吧.. 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1540 http://poj.org/problem?id=2892 ~~~~ 我的代码:200ms #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #defin

HDU 3308 LCIS (端点更新+区间合并)

刚刚做了两道LCIS,碰到这道线段树,脑抽了似的写 线段树+dp(LCIS),贡献一发TLE. 才想到要区间合并,query函数写了好久.下面有详细注释,参见代码吧~~欢迎点赞,欢迎卖萌~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 #include<cstdio> #inc

HDU 4902 线段树(区间更新)

Nice boat Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 353    Accepted Submission(s): 169 Problem Description There is an old country and the king fell in love with a devil. The devil alw

HDU 1698 线段树(区间染色)

Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 16255    Accepted Submission(s): 8089 Problem Description In the game of DotA, Pudge’s meat hook is actually the most horrible thing f

POJ 3667 线段树的区间合并简单问题

题目大意:有一排标号1-N的房间.操作一:询问是不是有连续长度为a的空房间,有的话住进最左边(占用a个房间)操作二:将[a,a+b-1]的房间清空(腾出b个房间)思路:记录每个区间中“靠左”“靠右”“中间”的空房间线段树操作:update:区间替换query:询问满足条件的最左端点 题目链接: http://vjudge.net/problem/viewProblem.action?id=10354 题目操作: 因为这里找从最左边住起的房间,所以这里不能像常规地写query函数那样写了,终于发现

Codeforces Round #222 (Div. 1) D. Developing Game 线段树有效区间合并

D. Developing Game Pavel is going to make a game of his dream. However, he knows that he can't make it on his own so he founded a development company and hired n workers of staff. Now he wants to pick n workers from the staff who will be directly res

hdu 3308 线段树

输入一串数字,有两个操作:Q a b 查询a到b区间内严格递增子串的最大长度 : U a b 把第a位数字替换成b .注意输入的编号是从0开始 解法:线段树维护区间的严格递增子串的最大长度即可.注意细节. #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; struct Tree{ int l,r; int val