http://poj.org/problem?id=2750
题意,给你n个数,首尾相接,让你求最大连续环区间和
首先知道这个最大连续和只有两种情况,
1是不跨越首尾的,这种直接求
2是跨越首尾的,显然最大连续和是跨越首尾的,那么最小连续区间和必然在1-n之间,所以我们只需要求最小连续和,然后sum-它就得到最大连续和
如果只求一次,可以dp就好了,但是由于每次动态更新单点,所以得用分治法求,在线段树上维护
一个区间的 最大连续和,最小连续和,最大连续前缀/后缀,最小连续前缀/后缀,区间和,共七个值(或许可以维护少几个。。。)
然后每次单点更新后,1是查询maxx[1],而是sum[1]-minn[1]。
注意是,题目要求不能把所有点都选上,所以如果是 1111的情况,只能选3个。
也就是如果最后maxx_ans==sum【1】,的话我们就删掉一个最小的值
这个最小值 我用set+一个vis数组维护。。直接用multiset也应该OK
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <set>
#include <map>
#include <vector>
#include <iostream>
using namespace std;
#define ll int
const double pi=acos(-1.0);
double eps=1e-5;
int max(int a,int b)
{return a>b?a:b;}
int min(int a,int b)
{return a<b?a:b;}
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int n,m;
const int maxn=100000+50;
int tm[maxn];
struct tree
{
int maxx[maxn<<2];
int suf_maxx[maxn<<2];
int pre_maxx[maxn<<2];
int pre_minn[maxn<<2];
int suf_minn[maxn<<2];
int minn[maxn<<2];
int sum[maxn<<2];
void PushUP(int rt)
{
sum[rt] =sum[rt<<1]+sum[rt<<1|1];
maxx[rt] =max( maxx[rt<<1],maxx[rt<<1|1]);
maxx[rt]=max(maxx[rt],suf_maxx[rt<<1]+pre_maxx[rt<<1|1]);
suf_maxx[rt] =max(suf_maxx[rt<<1]+sum[rt<<1|1],suf_maxx[rt<<1|1]);
pre_maxx[rt] =max(pre_maxx[rt<<1|1]+sum[rt<<1],suf_maxx[rt<<1]);
minn[rt] =min( minn[rt<<1],minn[rt<<1|1]);
minn[rt]=min(minn[rt],suf_minn[rt<<1]+pre_minn[rt<<1|1]);
pre_minn[rt]=min(pre_minn[rt<<1],sum[rt<<1]+pre_minn[rt<<1|1]);
suf_minn[rt]=min(suf_minn[rt<<1|1],sum[rt<<1|1]+suf_minn[rt<<1]);
}
void build(int l,int r,int rt)
{
if (l == r)
{
sum[rt]=pre_minn[rt]=suf_minn[rt]=suf_maxx[rt]=pre_maxx[rt]=minn[rt]=maxx[rt]=tm[r];
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUP(rt);
}
void update(int l,int r,int rt,int num,int val)
{
if (l == r&& r==num)
{
sum[rt]=pre_minn[rt]=suf_minn[rt]=suf_maxx[rt]=pre_maxx[rt]=minn[rt]=maxx[rt]=val;
return ;
}
int m = (l + r) >> 1;
if (num<=m)
update(lson,num,val);
else
update(rson,num,val);
PushUP(rt);
}
};
tree tp;
int vis[3000];
set<int> sb;
int main()
{
int i ;
int n;
cin>>n;
int minnnn=10000;
for (i=1;i<=n;i++)
{
scanf("%d",&tm[i]);
vis[tm[i]+1000]++;
if (vis[tm[i]+1000]==1)
sb.insert(tm[i]);
}
tp.build(1,n,1);
cin>>m;
int who,val;
for (i=1;i<=m;i++)
{
scanf("%d%d",&who,&val);
tp.update(1,n,1,who,val); //更新val
vis[tm[who]+1000]--; //删除tm[who]
if (vis[tm[who]+1000]==0) //如果数量为零,从set抹除
sb.erase(tm[who]);
vis[val+1000]++; //增加val个数,如果是第一次,加入set
if (vis[val+1000]==1)
sb.insert(val);
tm[who]=val; //更新tm[who]
int ret_maxx=tp.maxx[1];
int ret_minn=tp.minn[1];
ret_minn =tp.sum[1]-ret_minn ;
int ans=max(ret_maxx,ret_minn);
if (ans==tp.sum[1])
ans-=*(sb.begin());
printf("%d\n",ans);
}
return 0;
}

&spm=1001.2101.3001.5002&articleId=50791711&d=1&t=3&u=b1b86321a776483f8adbfa96a9a3c614)
345

被折叠的 条评论
为什么被折叠?



