小白逛公园
Time Limit:20000MS Memory Limit:65536K
Case Time Limit:2000MS
Description
小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了。
一开始,小白就根据公园的风景给每个公园打了分-.-。小新为了省事,每次遛狗的时候都会事先规定一个范围,小白只可以选择第a个和第b个公园之间(包括a、b两个公园)选择连续的一些公园玩。小白当然希望选出的公园的分数总和尽量高咯。同时,由于一些公园的景观会有所改变,所以,小白的打分也可能会有一些变化。
那么,就请你来帮小白选择公园吧。
Input
第一行,两个整数N和M,分别表示表示公园的数量和操作(遛狗或者改变打分)总数。
接下来N行,每行一个整数,依次给出小白 开始时对公园的打分。
接下来M行,每行三个整数。第一个整数K,1或2。K=1表示,小新要带小白出去玩,接下来的两个整数a和b给出了选择公园的范围(1≤a,b≤N);K=2表示,小白改变了对某个公园的打分,接下来的两个整数p和s,表示小白对第p个公园的打分变成了s(1≤p≤N)。
其中,1≤N≤500 000,1≤M≤100 000,所有打分都是绝对值不超过1000的整数。
Output
小白每出去玩一次,都对应输出一行,只包含一个整数,表示小白可以选出的公园得分和的最大值。
Sample Input
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 2 3
Sample Output
2
-1
Source
vijos
#include <stdio.h>
#define maxn 500000
#define inf -2100000000
inline void _read(int& d)
{
char t=getchar();bool f=false;
while(t<'0'||t>'9') {if(t=='-') f=true; t=getchar();}
for(d=0;t<='9'&&t>='0';t=getchar()) d=d*10+t-'0';
if(f) d=-d;
}
inline void _out(int d)
{
int o[30],top=1;
if(d==0){putchar('0');return ;}
if(d<0) {putchar('-');d=-d;}
while(d)
{
o[top++]=d%10;
d/=10;
}
for(--top;top;--top) putchar('0'+o[top]);
}
struct node{int a, b, l, r, lmax, rmax, max, sum;}tree[maxn*2+9];
int mark[maxn+9], tot=0;
int rofmax2(int a, int b)
{
if(a>b) return a;
return b;
}
int rofmax3(int a, int b, int c)
{
return rofmax2(rofmax2(a, b), c);
}
int Min(int a, int b)
{
if(a<b) return a;
return b;
}
void updata(int now)
{
//以下全部维护当前区间的数据
int ls=tree[now].l, rs=tree[now].r;
tree[now].sum=tree[ls].sum+tree[rs].sum; //维护区间和
tree[now].lmax=rofmax2(tree[ls].lmax, tree[ls].sum+tree[rs].lmax); //维护左起最长连续和
tree[now].rmax=rofmax2(tree[rs].rmax, tree[rs].sum+tree[ls].rmax); //维护右起最长连续和
tree[now].max=rofmax3(tree[ls].max, tree[rs].max, tree[ls].rmax+tree[rs].lmax); //维护最长区间和
}
void maketree(int x, int y)
{
int now=++tot;
tree[now].a=x; tree[now].b=y;
tree[now].lmax=tree[now].rmax=tree[now].max=tree[now].sum=inf;
if(x==y)
{
tree[now].l=0;
tree[now].r=0;
tree[now].max=tree[now].lmax=tree[now].rmax=tree[now].sum=mark[x];
return;
}
tree[now].l=tot+1;
maketree(x, (x+y)/2);
tree[now].r=tot+1;
maketree((x+y)/2+1, y);
updata(now);
}
int _search_right(int i, int l, int r)
{
int t=-2100000000;
if(l<=tree[i].a) return tree[i].rmax;
if(tree[i].r)t=rofmax2(t, _search_right(tree[i].r, l, r));
if(tree[i].l&&l<=tree[tree[i].l].b)t=rofmax2(t,tree[tree[i].r].sum+_search_right(tree[i].l, l, r));
return t;
}
int _search_left(int i, int l, int r)
{
int t=-2100000000;
if(r>=tree[i].b)return tree[i].lmax; //如果当前节点包含于[l, r], 返回当前节点的左起最大连续和
if(tree[i].l)t=rofmax2(t, _search_left(tree[i].l, l, r));
if(tree[i].r&&r>=tree[tree[i].r].a)t=rofmax2(t, tree[tree[i].l].sum+_search_left(tree[i].r, l, r));
return t;
}
int getans(int p, int l, int r)
{
if(l<=tree[p].a&&tree[p].b<=r) return tree[p].max; //如果当前节点包含于[l, r], 返回当前节点的最长连续和
int mid=(tree[p].a+tree[p].b)/2, lmax=inf, rmax=inf;
if(l<=mid) lmax=getans(tree[p].l, l, r); //左儿子与[l, r]区间相交部分的最长连续和
if(r>mid) rmax=getans(tree[p].r, l, r); //有儿子与[l, r]区间相交部分的最长连续和
int lt=tree[p].l, rt=tree[p].r;
int lenm=-2100000000;
if(l<=tree[lt].b&&tree[rt].a<=r) //如果左右儿子都与[l, r]相交,
{
lenm=
_search_left(rt, l, r) //固定左边界向右搜索最大连续和
+
_search_right(lt, l, r); //固定右边界向左搜索最大连续和
}
return rofmax2(lmax, rofmax2(rmax, lenm));
}
void change(int now, int k, int s) //第k个公园的分数变为s,当前讨论到 now 点
{
if(tree[now].a<=k&&k<=tree[now].b) //如果第k个公园在当前节点表示的区间之内
{
if(tree[now].a==tree[now].b) //如果当前节点是叶节点
{
tree[now].lmax=tree[now].rmax=tree[now].max=tree[now].sum=s;
}
else //如果当前节点不是叶节点
{
change(tree[now].l, k, s);
change(tree[now].r, k, s);
updata(now);
}
}
}
int main()
{
int n, m, alt, a, b, p, s;
_read(n);_read(m); //n个公园,m个操作
for(int i=1; i<=n; i++) //读入每个公园的分数
{
_read(mark[i]);
}
maketree(1, n); //建树
for(int i=1; i<=m; i++)
{
_read(alt);
if(1==alt)
{
_read(a); _read(b); //选择公园的范围[a, b]
if(a>b) //有可能a>b, 交换a,b
{
int t=a;
a=b;
b=t;
}
_out(getans(1, a, b)); //输出[a, b]最长连续和
putchar('\n');
}
else
{
_read(p); _read(s);
change(1, p, s); //第p个公园的分变成s
}
}
return 0;
}