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; }