I Hate It

本文介绍了一种使用线段树解决区间查询与单点更新问题的方法。通过实例详细展示了线段树的构建、更新及查询过程。

I Hate It

Time Limit : 9000/3000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other)
Total Submission(s) : 16   Accepted Submission(s) : 10
Problem Description
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。 这让很多学生很反感。 不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
 

 

Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
 
Output
对于每一次询问操作,在一行里面输出最高成绩。
 
Sample Input

5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5

 
Sample Output

5
6
5
9

 
Author
linle
 
Source
2007省赛集训队练习赛(6)_linle专场
基础的线段树题目,单点更新,区间最值
#include <stdio.h>
#include <stdlib.h>
 struct Line
{
    int Left,Right;
    int Max;
};
struct Line ID[600200];
int MAX_NUM;
void Build(int l,int r,int n)
{
    int Mid=(l+r)/2;
    ID[n].Left=l;
    ID[n].Right=r;
    if (l==r)
    {scanf("%d",&ID[n].Max);return;}
    Build(l,Mid,2*n);
    Build(Mid+1,r,2*n+1);
    ID[n].Max=(ID[2*n].Max>=ID[2*n+1].Max)?ID[2*n].Max:ID[2*n+1].Max;
}

void Updata(int l,int r,int n,int data,int S)
{
    int Mid=(l+r)/2;
    if (l==r&&l==S)
    {ID[n].Max=data;return;}
    if(S<=Mid)
        Updata(l,Mid,2*n,data,S);
    else
        Updata(Mid+1,r,2*n+1,data,S);
    ID[n].Max=(ID[2*n].Max>=ID[2*n+1].Max)?ID[2*n].Max:ID[2*n+1].Max;
}

void Search(int r,int l,int Step)
{
    int Mid;
    if (r==ID[Step].Left && l==ID[Step].Right)
    {
        if(ID[Step].Max>MAX_NUM)
            MAX_NUM=ID[Step].Max;
        return;
    }
    if (ID[Step].Left==ID[Step].Right)   return;
    Mid=(ID[Step].Left+ID[Step].Right)/2;
    if (Mid>=l)    Search(r,l,Step*2);
    else if (Mid<r)    Search(r,l,Step*2+1);
    else
    {
        Search(r,Mid,Step*2);
        Search(Mid+1,l,Step*2+1);
    }
}

int main()
{
    int M,N,a,b;
    char Change;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        Build(1,N,1);
        getchar();
        while(M--)
        {
            scanf("%c%d%d",&Change,&a,&b);
            MAX_NUM=0;
            if(Change=='Q')
            {
                Search(a,b,1);
                printf("%d\n",MAX_NUM);
            }
            else if(Change=='U')
                Updata(1,N,1,b,a);
            getchar();
        }
    }
    return 0;
}
View Code

修改:2015.5.15

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <algorithm>
 5 using namespace std;
 6 /*Build(int r,int l,int n)   [r,l]为所要初始化的区间,建立一颗二叉树,n初始值为1*/
 7 /*Updata(int n)     更新节点上的信息,返回左右孩子中关键值的和*/
 8 /*Change(int r,int l,int Step,int data) ,初始赋值Step=1,把该区间[r,l]或者点[a,a]上的关键值累加data*/
 9 /*Search(int r,int l,int Step)  搜索区间,初始赋值Step=1,[r,l]为需要拿去匹配的空间,获取该区间上关键值总和*/
10 int SIGN;            /*,起初赋值为0,用来保存关键值得总数*/
11 typedef struct Line
12 {
13     int Left,Right;
14     int Key;
15 }LR;
16 LR ID[1008600];/*每一个节点都需要一个存储*/
17 int Up_Key(int n)/*更新当前点,更新最大值*/
18 {
19     return max(ID[2*n].Key,ID[2*n+1].Key);/*更新关键值(最大值)*/
20 }
21 void Build(int L,int R,int n)    /*输入时建立二叉树根*/
22 {
23     int Mid=(L+R)/2;
24     ID[n].Left=L;ID[n].Right=R;
25     if (L==R)                /*找到根节点的时候输入*/
26     {scanf("%d",&ID[n].Key);return;}
27     Build(L,Mid,2*n);
28     Build(Mid+1,R,2*n+1);
29     ID[n].Key=Up_Key(n);
30 }
31 void UpData(int N,int Step,int data)/*单点更新,单点修改*/
32 {
33     int Mid;
34     if(N==ID[Step].Left&&N==ID[Step].Right)
35     {
36         ID[Step].Key=data;return;/*单点修改成data*/
37     }
38     Mid=(ID[Step].Left+ID[Step].Right)/2;
39     if (Mid>=N)UpData(N,Step*2,data);
40     else UpData(N,Step*2+1,data);
41     ID[Step].Key=Up_Key(Step);
42 }
43 void Search(int L,int R,int Step)/*匹配区间*/
44 {
45     int Mid;
46     if(L==ID[Step].Left && R==ID[Step].Right)
47     {
48         SIGN=max(ID[Step].Key,SIGN);return;/*获取最大值*/
49     }
50     Mid=(ID[Step].Left+ID[Step].Right)/2;
51     if (Mid>=R)     Search(L,R,Step*2);
52     else if (Mid<L)    Search(L,R,Step*2+1);
53     else
54     {
55         Search(L,Mid,Step*2);
56         Search(Mid+1,R,Step*2+1);
57     }
58 }
59 int main()
60 {
61     int N,M,i;
62     while(scanf("%d%d",&N,&M)!=EOF)
63     {
64         Build(1,N,1);
65         while(M--)
66         {
67             char Str;
68             int A,B;
69             scanf(" %c %d%d",&Str,&A,&B);
70             if(Str=='Q')
71             {
72                 SIGN=0;
73                 Search(A,B,1);
74                 printf("%d\n",SIGN);
75             }
76             else
77             {
78                 UpData(A,1,B);
79             }
80         }
81     }
82     return 0;
83 }
View Code

 更新模板:2016.4.14

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <string.h>
 4 #include <algorithm>
 5 #define MaxN 1008600
 6 using namespace std;
 7 struct Node{int L,R,Key;}ID[MaxN];/*每一个节点都需要一个存储*/
 8 int Up_Date(int n){return max(ID[2*n].Key,ID[2*n+1].Key);}
 9 void Build(int L,int R,int n)/*输入时建立二叉树根,Build(1,N,1)*/
10 {
11     ID[n].L=L;ID[n].R=R;
12     if (L==R){
13         scanf("%d",&ID[n].Key);return;
14     }
15     int Mid=(L+R)/2;
16     Build(L,Mid,2*n);Build(Mid+1,R,2*n+1);
17     ID[n].Key=Up_Date(n);
18 }
19 void UpDate_P(int P,int n,int v) /*单点更新*/
20 {
21     if(P==ID[n].L&&P==ID[n].R){
22         ID[n].Key=v;return;/*单点加上Data*/
23     }
24     int Mid=(ID[n].L+ID[n].R)/2;
25     if (Mid>=P)UpDate_P(P,n*2,v);
26     else UpDate_P(P,n*2+1,v);
27     ID[n].Key=Up_Date(n);
28 }
29 
30 int Query(int L,int R,int n)/*匹配询问区间*/
31 {
32     if(L==ID[n].L && R==ID[n].R){
33         return ID[n].Key;
34     }
35     int Mid=(ID[n].L+ID[n].R)/2;
36     if (Mid>=R)return Query(L,R,n*2);
37     else if (Mid<L)return Query(L,R,n*2+1);
38     else{
39         return max(Query(L,Mid,n*2),Query(Mid+1,R,n*2+1));
40     }
41 }
42 int Num[100860];
43 int main()
44 {
45     char Str;
46     int T,t=1,N,M,A,B;
47     while(scanf("%d%d",&N,&M)!=EOF)
48     {
49         Build(1,N,1);
50         while(M--){
51             scanf(" %c%d%d",&Str,&A,&B);
52             switch(Str){
53                 case 'Q':printf("%d\n",Query(A,B,1));break;
54                 case 'U':UpDate_P(A,1,B);break;
55             }
56         }
57     }
58     return 0;
59 }
View Code

 

转载于:https://www.cnblogs.com/Wurq/articles/3869939.html

<think>好的,我现在需要帮助用户分析他们在洛谷P1531题目上使用C++线段树代码时出现运行时错误(RE)的可能原因。首先,我应该回忆一下RE的常见原因,比如数组越界、递归过深、指针错误、除零错误等。但用户提到的是线段树,所以需要结合线段树的结构和洛谷题目的具体要求来分析。 首先,我需要了解洛谷P1531的具体题目要求。虽然用户没有给出题目内容,但根据常见的线段树问题,P1531可能涉及到区间查询或更新操作。例如,可能是区间最大值、最小值或和的问题。根据引用[4]中的提示,线段树的节点数需要足够大,否则可能导致数组越界。比如,如果线段树的大小没有开到4倍原始数组大小,可能会导致RE。例如,引用[4]中提到必须开满$2^{21}$的节点数,否则会RE。这可能是因为线段树的层数不够,导致访问到未分配的内存。 接下来,用户提供的代码示例中,引用的代码1展示了线段树的构建过程,其中结构体数组tr的大小是N*4。这通常是为了确保足够的节点数。如果用户在代码中没有正确设置数组大小,比如N的值不够大,或者线段树数组没有开到4*N,可能会导致数组越界。例如,如果用户的问题中N是1e5,那么线段树数组应该开到4*1e5,否则在递归过程中可能访问到超出数组范围的下标,导致RE。 其次,检查递归的终止条件。在build函数中,当l == r时,构造叶子节点。如果这里的条件不正确,可能导致无限递归,最终栈溢出。例如,在分割区间时,如果中间值计算错误,比如使用(l + r) >> 1时,若l和r的和超过int的范围,可能导致中间值错误,进而导致区间划分错误,无限递归或无效的节点访问。 另外,引用[3]中提到可持久化线段树的实现中需要注意节点的更新方式,可能涉及左右子节点的正确传递。虽然用户的问题可能不是可持久化线段树,但普通线段树中,如果左右子节点的计算有误,比如左孩子应该是2*p,而用户代码中使用了错误的计算方式,可能导致访问无效的节点位置。例如,在引用[1]中,左孩子是p<<1,右孩子是p<<1|1,这对应于2*p和2*p+1。如果用户代码中的宏定义有误,比如lc和rc的计算错误,会导致访问到错误的子节点,进而访问数组越界。 此外,输入数据的处理也可能导致问题。例如,在main函数中,用户读取n之后,如果输入的w数组是从1开始索引的,但n的值超过实际数组的大小,会导致数组越界。例如,如果w数组的大小是N,而用户输入的n超过了N,就会导致访问w[n]时越界,引发RE。 还需要考虑题目的数据范围。如果题目中的数据范围很大,比如N是1e5,而线段树数组的大小不足,比如只开到1e5,显然不够,必须至少是4*N。例如,在引用[4]中提到,节点数必须足够,否则会RE。用户需要确认他们的线段树数组是否足够大,以容纳所有可能的节点。 另外,检查是否有未初始化的变量。例如,在结构体node中,如果sum未正确初始化,或者在build函数中没有正确赋值,可能导致未定义行为,进而引发错误。例如,在build函数中,如果tr[p].sum的计算错误,可能在后续的查询或更新操作中出现问题,但这是否直接导致RE还需看具体实现。 此外,递归深度过大也可能导致栈溢出。例如,如果题目中的n很大,比如1e5,那么递归构建线段树可能会超出系统栈的大小限制,导致RE。这种情况下,可以考虑改为非递归实现,或者调整编译器的栈大小。但通常在线段树问题中,递归深度是O(log n),对于n=1e5来说,深度大约为20层,应该不会导致栈溢出。但用户如果使用了非标准的递归方式,可能存在问题。 还有可能是在处理查询或更新操作时,区间范围不正确。例如,在build函数中,分割区间为左闭右闭区间,而在递归时,如果分割点m计算错误,可能导致子区间的范围错误,例如左区间是[l, m],右区间是[m+1, r]。如果这里的m计算错误,可能导致子区间的r < l,从而在后续处理中出现非法区间,触发断言或越界。 另外,检查是否有除零操作。例如,在计算中间值m时,如果l和r相等,可能导致除以零的情况。但通常线段树的构建中,当l == r时已经终止递归,不会出现这种情况。 总结可能的原因: 1. 线段树数组大小不足,导致访问越界。 2. 递归过程中区间分割错误,导致无效的子节点访问。 3. 输入数据超出数组声明的范围。 4. 左右子节点计算错误,如宏定义错误。 5. 递归深度过大导致栈溢出(可能性较低,但需确认n的范围)。 6. 未正确处理边界条件,如l > r的情况。 根据引用[4]中的强调,节点数必须足够,这可能是主要原因。此外,引用[1]中的代码展示了正确的左右子节点计算方式,如果用户的代码在此处有误,也会导致问题。因此,建议用户首先检查线段树数组的大小是否足够,比如是否开到了4*N,并确认左右子节点的计算是否正确。同时,检查输入的n是否超过了数组w的大小,以及区间分割是否正确。</think>在洛谷P1531题目中使用线段树出现运行时错误(RE)的可能原因分析如下: ### 1. **线段树数组大小不足** 线段树需要存储$4 \times n$的节点空间。若数组`tr[N*4]`的$N$未覆盖题目数据范围,会导致越界访问[^4]。 例如,当$n=5 \times 10^5$时,需保证`N`至少为$5 \times 10^5$,且数组大小设置为$N \times 4$。 ### 2. **递归区间划分错误** 在`build`函数中,计算中间值`m`时若使用`int m = (l + r) >> 1`,需确保`l + r`不会溢出。若题目数据范围超过$10^9$,应改为: ```cpp int m = l + (r - l) / 2; ``` 否则可能导致无效区间划分,例如子区间出现$l > r$的情况。 ### 3. **输入数据越界** 若`w`数组从下标1开始存储,但输入的$n$超过数组声明大小,会触发越界。需检查代码中: ```cpp int w[N]; // 是否足够大? cin >> n; for (int i = 1; i <= n; i++) cin >> w[i]; // 若n > N-1则越界 ``` ### 4. **左右子节点编号计算错误** 左右子节点编号必须严格遵循$2p$和$2p+1$的规则[^1]。若宏定义错误: ```cpp #define lc p << 2 // 错误示例(应为p << 1) ``` 会导致访问无效内存区域。 ### 5. **未处理边界条件** 例如,在查询或更新操作中,若区间参数未满足$1 \leq l \leq r \leq n$的限制,可能触发非法访问。需在函数入口添加: ```cpp if (l > r || r < 1 || l > n) return 0; // 根据具体逻辑调整 ``` ### 6. **递归深度过大(栈溢出)** 虽然线段树递归深度为$O(\log n)$,但若$n$极大(如$2^{20}$),可能需手动扩栈。在C++代码开头添加: ```cpp #pragma comment(linker, "/STACK:1024000000,1024000000") ``` ### 验证建议 1. **本地测试案例** 使用洛谷题目中的经典测试案例(如引用[5]中的输入数据),检查是否输出预期结果。 2. **打印调试信息** 在`build`函数中输出区间信息,观察递归过程是否出现$l > r$或越界下标。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值