POJ 2750 Potted Flower

20 篇文章 0 订阅
12 篇文章 0 订阅

题目大意:给定一长度为n的环形序列求最大连续和,连续和长度不能超过n-1,单点修改。

最大连续和?我会动规/贪心

环形序列?我会乱搞

带修改?我会线段树

限制长度?!@#¥%……&*()

我们先忽略掉环形序列

首先,限制很特殊,是n-1,于是我们可以很简单地求出[1,n-1]和[2,n]的最大连续和,取两者的最大值。(不要问我限制为k的时候怎么做,我不会啊)

然后我们看环形序列,首先考虑静态做法,嗯,看来我以前都是乱搞的TAT,按照我以前的做法,发现当前长度超过n时就把尾巴砍砍砍。

考虑最大连续和的对偶问题:最小连续和。

显然,环形序列的序列和减去最小连续和就是最大连续和了。

这里我们不妨规定最小连续和的最短长度为1,于是最大连续和的最大长度为n-1。

至此,此题差不多就解决了。

于是套用线段树的带修改最大连续和的方法,同样维护最小连续和,详见代码。

#include<iostream>
#include<cstdio>
#include<cstring>
#define lc o<<1
#define rc o<<1|1
using namespace std;
const int N=100000+5;
struct solver{
	int sum,lx,rx,li,ri,mx,mi;
};
solver operator + (solver l,solver r){
	solver ans;
	ans.sum=l.sum+r.sum;
	ans.lx=max(l.lx,l.sum+r.lx);
	ans.rx=max(r.rx,r.sum+l.rx);
	ans.li=min(l.li,l.sum+r.li);
	ans.ri=min(r.ri,r.sum+l.ri);
	ans.mx=max(l.rx+r.lx,max(l.mx,r.mx));
	ans.mi=min(l.ri+r.li,min(l.mi,r.mi));
	return ans;
}
struct Node{
	int l,r;
	solver ans;
}tr[N<<2];
int a[N],n;
void pushup(int o){
	tr[o].ans=tr[lc].ans+tr[rc].ans;
}
solver newsol(int x){
	solver ans=(solver){x,x,x,x,x,x,x};
	return ans;
}
void build(int o,int l,int r){
	tr[o].l=l;tr[o].r=r;
	if(l==r)tr[o].ans=newsol(a[l]);
	else{
		int mid=l+r>>1;
		build(lc,l,mid);build(rc,mid+1,r);
		pushup(o);
	}
}
solver query(int o,int a,int b){
	int l=tr[o].l,r=tr[o].r;
	if(a==l&&r==b)return tr[o].ans;
	else{
		int mid=l+r>>1;
		if(b<=mid)return query(lc,a,b);
		else if(mid<a)return query(rc,a,b);
		else return query(lc,a,mid)+query(rc,mid+1,b);
	}
}
void update(int o,int p,int v){
	int l=tr[o].l,r=tr[o].r;
	if(l==r)tr[o].ans=newsol(v);
	else{
		int mid=l+r>>1;
		if(p<=mid)update(lc,p,v);
		else update(rc,p,v);
		pushup(o);
	}
}
int solve(){
	int ans=tr[1].ans.sum-tr[1].ans.mi;
	ans=max(ans,query(1,1,n-1).mx);
	ans=max(ans,query(1,2,n).mx);
	return ans;
}
int main(){
	//freopen("a.in","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	build(1,1,n);
	int m;scanf("%d",&m);
	while(m--){
		int p,v;scanf("%d%d",&p,&v);
		update(1,p,v);
		printf("%d\n",solve());
	}
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值