UVA 1400

本文详细阐述了一个经典的线段树应用案例,作者通过解决一个复杂的问题,分享了线段树的基本原理、实现细节以及遇到的挑战。文章深入探讨了线段树在处理区间最大值和连续子数组和问题上的优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4146


经典线段树。。真的很经典的一个题。但是我断断续续写了接近两天才AC。主要是找BUG。。很多东西没有考虑完全,就开始写了。

在那个没有思考完全的代码下修改,太蛋碎了。


主要存放 3个东西,一个最长前缀。pre_max,  区间连续的最大和,all_max,还有存放后缀 suf_max;

一段区间的all_max可能由以下5个组成。

1   左区间的  all_max;  

2   右区间的all_max;

3   左边的sum+右区间的pre_max;

4   右边的sum+左边的suf_max;

5  左边的suf_max+右边的pre_max;

把这个主体想清楚。其他的差不多。然后要注意边界。


#include <cmath>
#include <ctime>
#include <iostream>
#include <string>
#include <vector>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cctype>
#include <stack>
#include <deque>
using namespace std;
typedef long long LL;
#define eps 10e-9
#define inf 0x3f3f3f3f
const int maxn = 500000+100;
LL a[maxn];
struct node{
    int l,r;
    int pr,sl;
    LL sum,pre_max,all_max,suf_max;
}ans[maxn<<2];
void pushup(int id,int l,int r){
    int m=(l+r)>>1;
    ans[id].sum=ans[id<<1].sum+ans[id<<1|1].sum;

    ans[id].pre_max=ans[id<<1].pre_max;
    ans[id].pr=ans[id<<1].pr;
    ans[id].suf_max=ans[id<<1|1].suf_max;
    ans[id].sl=ans[id<<1|1].sl;

    ans[id].all_max=ans[id<<1].sum+ans[id<<1|1].pre_max;
    ans[id].l=l;
    ans[id].r=ans[id<<1|1].pr;
    if(ans[id].all_max<ans[id<<1].all_max||(
       ans[id].all_max==ans[id<<1].all_max&&(ans[id].l==ans[id<<1].l)  )){
    ans[id].all_max=ans[id<<1].all_max;
    ans[id].l=ans[id<<1].l;
    ans[id].r=ans[id<<1].r;
    }
    if(ans[id<<1].suf_max+ans[id<<1|1].pre_max>ans[id].all_max||
       (ans[id<<1].suf_max+ans[id<<1|1].pre_max==ans[id].all_max&&ans[id<<1].sl<ans[id].l)){
        ans[id].all_max=ans[id<<1].suf_max+ans[id<<1|1].pre_max;
        ans[id].l=ans[id<<1].sl;
        ans[id].r=ans[id<<1|1].pr;
    }
    if(ans[id].all_max<ans[id<<1|1].sum+ans[id<<1].suf_max||
      (ans[id].all_max==ans[id<<1|1].sum+ans[id<<1].suf_max&&ans[id<<1].sl<ans[id].l)){

       ans[id].all_max=ans[id<<1|1].sum+ans[id<<1].suf_max;
       ans[id].l=ans[id<<1].sl;
       ans[id].r=r;
    }
     if(ans[id].all_max<ans[id<<1|1].all_max||
        ans[id].all_max==ans[id<<1|1].all_max&&ans[id].l>ans[id<<1].sl  ){
       ans[id].all_max=ans[id<<1|1].all_max;
       ans[id].l=ans[id<<1|1].l;
       ans[id].r=ans[id<<1|1].r;
    }
    if(ans[id<<1].sum+ans[id<<1|1].pre_max>ans[id].pre_max){
      ans[id].pre_max=ans[id<<1].sum+ans[id<<1|1].pre_max;
      ans[id].pr=ans[id<<1|1].pr;
    }
    if(ans[id<<1|1].sum+ans[id<<1].suf_max>=ans[id].suf_max){
       ans[id].suf_max=ans[id<<1|1].sum+ans[id<<1].suf_max;
       ans[id].sl=ans[id<<1].sl;
    }

}
void build(int id,int l,int r){
    if(l==r){
      ans[id].sum=ans[id].pre_max=ans[id].all_max=ans[id].suf_max=a[l];
      ans[id].l = ans[id].r= ans[id].pr=ans[id].sl=l; return ;
    }
    int m=(l+r)>>1;
    build(id<<1,l,m);  build(id<<1|1,m+1,r);
    pushup(id,l,r);
}
node query(int id,int l,int r,int L,int R){
    if(l==L&&r==R){
       return ans[id];
    }
    int m=(L+R)>>1;
    if(r<=m)     return  query(id<<1,l,r,L,m);
    else if(l>m) return  query(id<<1|1,l,r,m+1,R);
    else{
        node t1=query(id<<1,l,m,L,m);
        node t2=query(id<<1|1,m+1,r,m+1,R);
        node t=t1; t.sl=t2.sl;
        t.suf_max=t2.suf_max;
        t.sum=t1.sum+t2.sum;

        t.all_max=t1.sum+t2.pre_max;
        t.l=l; t.r=t2.pr;

        if(t.all_max<t1.all_max||(t.all_max==t1.all_max&&t.l==t1.l) ){
           t.all_max=t1.all_max;
           t.l=t1.l;
           t.r=t1.r;
        }
         if(t1.suf_max+t2.pre_max>t.all_max||
            (t1.suf_max+t2.pre_max==t.all_max&&t1.sl<t.l)){
           t.all_max=t1.suf_max+t2.pre_max;
           t.l=t1.sl;
           t.r=t2.pr;
        }
        if(t2.sum+t1.suf_max>t.all_max||
           (t2.sum+t1.suf_max==t.all_max&&t1.sl<t.l)){
           t.all_max=t1.suf_max+t2.sum;
           t.l=t1.sl;
           t.r=r;
        }
        if(t2.all_max>t.all_max){
           t.all_max=t2.all_max;
           t.l=t2.l;
           t.r=t2.r;
        }

        if(t1.sum+t2.pre_max>t.pre_max){
           t.pre_max=t1.sum+t2.pre_max;
           t.pr=t2.pr;
        }

        if(t2.sum+t1.suf_max>=t.suf_max){
           t.suf_max=t2.sum+t1.suf_max;
           t.sl=t1.sl;
        }
        return t;
    }
}
int main(){
    int n,m,l,r,ca=1;
    while(scanf("%d%d",&n,&m)!=EOF){
        for(int i=1;i<=n;i++)  scanf("%lld",&a[i]);
        build(1,1,n);
        printf("Case %d:\n",ca++);
        for(int i=0;i<m;i++){
            scanf("%d %d",&l,&r);
            node t=query(1,l,r,1,n);
         //   printf("%d %d %lld %lld %lld\n",t.l,t.r,t.all_max,t.pre_max,t.suf_max);
            printf("%d %d\n",t.l,t.r);
        }
    }
    return 0;
}










#include <bits/stdc++.h> #define ll long long #define lb(x) x&(-x) using namespace std; const ll N=2e5; struct node{ ll l,r,suml,sumr,ans; }tr[4*N+5]; ll n,m,t; ll yy[N+5],pp[N+5],r[N+5],rr[N+5]; ll get(ll x,ll y){ ll s=0,ss=0; // cout<<x<<" "<<y<<" "; for(ll i=y-1;i;i-=lb(i)){ s+=rr[i]; } for(ll i=x-1;i;i-=lb(i)){ ss+=rr[i]; } // cout<<s-ss<<endl; return s-ss; } void updata(ll p,ll l,ll r){ ll mid=(l+r)>>1; tr[p].suml=min(tr[2*p].suml+get(mid,r),tr[2*p+1].suml); tr[p].sumr=min(tr[2*p+1].sumr+get(l,mid+1),tr[2*p].sumr); tr[p].ans=tr[2*p].suml+tr[2*p+1].sumr+get(mid,mid+1); tr[p].ans=min(tr[p].ans,min(tr[2*p].ans,tr[2*p+1].ans)); return; } void build(ll p,ll l,ll r){ if(l==r){ tr[p].l=l; tr[p].r=r; tr[p].suml=yy[l]; tr[p].sumr=pp[l]; tr[p].ans=yy[l]+pp[l]; return; } ll mid=(l+r)>>1; build(2*p,l,mid); build(2*p+1,mid+1,r); tr[p].l=tr[2*p].l; tr[p].r=tr[2*p+1].r; updata(p,tr[p].l,tr[p].r); return; } void change(ll p,ll x){ if(tr[p].l==tr[p].r){ tr[p].suml=yy[x]; tr[p].sumr=pp[x]; tr[p].ans=yy[x]+pp[x]; return; } ll mid=(tr[p].l+tr[p].r)>>1; if(x<=mid) change(2*p,x); else change(2*p+1,x); updata(p,tr[p].l,tr[p].r); return; } int main(){ scanf("%lld%lld%lld",&n,&m,&t); for(ll i=1;i<=n;i++) scanf("%lld",&yy[i]); for(ll i=1;i<=n;i++) scanf("%lld",&pp[i]); for(ll i=1;i<n;i++){ scanf("%lld",&r[i]); for(ll j=i;j<n;j+=lb(j)) rr[j]+=r[i]; } build(1,1,n); // for(ll i=1;i<=40;i++){ // cout<<tr[i].l<<" "<<tr[i].r<<" "; // cout<<tr[i].suml<<" "<<tr[i].sumr<<" "<<tr[i].ans<<endl; // } // return 0; printf("%lld\n",tr[1].ans); while(m--){ char op; ll x,y; cin>>op; scanf("%lld%lld",&x,&y); if(op=='r'){ for(ll i=x;i<n;i+=lb(i)){ rr[i]-=r[x]; rr[i]+=y; } r[x]=y; change(1,x),change(1,x+1); } else if(op=='p'){ pp[x]=y; change(1,x); } else{ yy[x]=y; change(1,x); } printf("%lld\n",tr[1].ans); } return 0; } ## **题目描述** 阿杰在游戏中管理着 $n$ 座编号为 $1$ 至 $n$ 的城市。每座城市有一个矿场和一个发电厂。他需要完成以下过程: 1. **采矿**:在任意城市 $i$ 的矿场开采矿物,花费 $y_i$(可为负,表示收益)。 2. **运输**:通过道路将矿物运送到任意城市 $j$ 的发电厂。相邻城市 $i$ 与 $i+1$ 间的道路费用为 $r_i$(双向且相同,可为负但不允许重复经过同一条道路)。 3. **发电**:在城市 $j$ 的发电厂消耗矿物发电,花费 $p_j$(可为负)。 **总花费**为三部分之和:$y_i + p_j + \text{运输路径费用}$。目标是选择起点 $i$ 和终点 $j$,使总花费最小(负花费表示净收益)。 游戏会动态更新费用:共 $m$ 个事件,每个事件将某个 $y_i$、$p_i$ 或 $r_i$ 修改为 $c$。需要实时输出每次更新后的最小总花费。 ## **输入格式** - **第一行**:$n, m, T$(城市数、事件数、测试点编号)。 - **第二行**:$n$ 个整数 $y_1, y_2, \dots, y_n$(采矿费用)。 - **第三行**:$n$ 个整数 $p_1, p_2, \dots, p_n$(发电费用)。 - **第四行**:$n-1$ 个整数 $r_1, r_2, \dots, r_{n-1}$(道路费用,$r_i$ 连接城市 $i$ 和 $i+1$)。 - **接下来 $m$ 行**:每行格式为 `t i c`: - $t \in \{ \texttt{y}, \texttt{p}, \texttt{r} \}$ 表示修改的费用类型。 - $i$ 表示被修改的城市编号($t=\texttt{r}$ 时 $i$ 表示道路编号,范围 $1 \le i \le n-1$)。 - $c$ 为新费用值(绝对值 $\le 10^{12}$)。 ## **输出格式** - **共 $m+1$ 行**: - 第 $1$ 行:初始状态的最小花费。 - 第 $2$ 至 $m+1$ 行:每次事件后的最小花费。 ## **样例** #### 输入样例 ```plaintext 10 10 0 12 12 17 28 42 55 60 73 73 91 89 89 79 77 54 38 34 24 24 0 2 4 2 1 4 3 7 10 10 p 2 92 p 3 87 r 4 3 r 1 9 r 2 4 r 5 3 y 10 72 y 8 67 r 2 0 y 4 91 ``` #### 输出样例 ```plaintext 53 53 53 55 55 55 54 54 54 50 50 ``` ## **数据范围** | 测试点 | $n$ | $m$ | 费用可为负 | $y_i=0$ | $p_i=0$ | |:------:|:-----:|:-------:|:------:|:----:|:-------:| | 1 | $500$ | $500$ | 否 | 否 | 否 | | 2 | $500$ | $500$ | 是 | 否 | 否 | | 3 | $3000$ | $3000$ | 否 | 否 | 否 | | 4 | $3000$ | $3000$ | 是 | 否 | 否 | | 5 | $2\times10^5$ | $2\times10^5$ | 是 | 是 | 是 | | 6–10 | $2\times10^5$ | $2\times10^5$ | 混合 | 混合 | 混合 | **特殊约束**: - 若“费用可为负”为**否**,则所有费用(含修改后)$\ge 0$。 - 若 $y_i=0$ 为**是**,则所有 $y_i=0$ 且无 `y` 类型事件;$p_i=0$ 同理。 --- ### **题目补充说明** 1. **道路特性**: - 道路双向通行且费用相同,但**不允许重复使用**(即使 $r_i<0$ 也不能多次经过同一条路获利)。 - 运输路径是简单路径(无环路)。 2. **发电过程**: - 采矿和发电必须在**不同或相同城市**完成(即 $i$ 和 $j$ 可相同,此时运输费用为 $0$)。 - 总花费计算式:$\text{min}_{1 \le i,j \le n} \left\{ y_i + p_j + \text{dis}(i,j) \right\}$,其中 $\text{dis}(i,j)$ 是 $i$ 到 $j$ 的路径费用和。 3. **事件限制**: - `r i c` 事件中 $i$ 为道路编号($1 \le i \le n-1$),修改连接 $i$ 和 $i+1$ 的道路费用。 代码答案错误,找出问题
最新发布
08-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值