题目链接: Intel Code Challenge Elimination Round C. Destroying Array
方法一(在线):
直接套用线段树动态维护区间最大连续和的模板,将一个数删去等价于改成无穷小。
注意INF必须足够大(也不能太大,建议贴着开,否则会超过LONG LONG)
时间复杂度O(nlogn),是一种可以通过的在线算法,代码量略大。
代码:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
using namespace std;
const LL maxn=100000+5,inf=100000LL*1000000000LL+5;
LL n;
inline void _read(LL &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
struct node{
LL Lmax,Rmax,Max,Sum;
}val[maxn*5];
LL s[maxn],sum[maxn],ql,qr,p;
LL getmax(LL x,LL y,LL z){
if(x>=z&&x>=y) return x;
if(y>=z) return y; return z;
}
void maintain(int o,int L,int R){
if(L==R) return ;
int mid=(L+R)>>1,ls=o*2,rs=o*2+1;
val[o].Sum=val[ls].Sum+ val[rs].Sum;
val[o].Lmax=val[ls].Lmax;
val[o].Rmax=val[rs].Rmax;
val[o].Lmax=max(val[o].Lmax ,+val[ls].Sum+val[rs].Lmax);
val[o].Rmax=max(val[o].Rmax ,val[rs].Sum+val[ls].Rmax);
val[o].Max=getmax(val[ls].Max,val[rs].Max,val[rs].Lmax+val[ls].Rmax);
}
void build(int o,int L,int R){
if(L==R)
val[o].Lmax=val[o].Rmax=val[o].Max=val[o].Sum=s[L];
else {
int mid=(L+R)>>1;
build(o*2,L,mid); build(o*2+1,mid+1,R);
val[o].Sum=sum[R]-sum[L-1];
maintain(o,L,R);
}
}
void update(int o,int L,int R){
if(L==R)
val[o].Lmax=val[o].Rmax=val[o].Max=val[o].Sum=s[L];
else {
int mid=(L+R)>>1;
if(p<=mid) update(o<<1,L,mid);
else update(o*2+1,mid+1,R);
maintain(o,L,R);
}
}
node query(int o,int L,int R){
//cout<<"visiting vertex # "<<o<<endl;
if(ql<=L&&qr>=R) return val[o];
int mid=(L+R)>>1;
if(qr<=mid) return query(o<<1,L,mid);
else if(ql>mid) return query(o*2+1,mid+1,R);
else if(ql<=mid&&qr>mid){
int ls=o*2,rs=ls+1;
node l=query(ls,L,mid);
node r=query(rs,mid+1,R);
node ans;
ans.Sum=l.Sum+r.Sum;
ans.Lmax=max(l.Lmax, l.Sum+r.Lmax);
ans.Rmax=max(r.Rmax, r.Sum+l.Rmax);
ans.Max=getmax(l.Rmax+r.Lmax, l.Max, r.Max);
return ans;
}
}
int main(){
int i,k,S;
_read(n);
for(i=1;i<=n;i++){
_read(s[i]);
sum[i]=sum[i-1]+s[i];
}
build(1,1,n);
for(i=1;i<=n;i++){
_read(p);
ql=1;qr=n ;
s[p]=-inf;
update(1,1,n);
LL ans=query(1,1,n).Max;
printf("%I64d\n",ans>-inf ? ans:0);
}
return 0;
}
方法2(离线):
考虑该过程反向进行,即每次加入一个数。
L[x]记录x向左最多可以延伸到的端点,
R[x]记录x向右最多可以延伸到的端点。
val[x]记录x所处线段的和。
每次用类似链表的方式维护,注意到添加一个数s[x]只会影响到s[x-1]和s[x+1]所在的区间和(也就是合并了这两个区间)。
每次更新最大和即可。
时间复杂度O(n),比方法一更优。
代码:
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
#define xx first
#define yy second
#define LL long long
#define CLEAR(xxx) memset(xxx,0,sizeof(xxx))
#define INTpair pair<int,int>
using namespace std;
const int maxn=100000+5,inf=1e9;
int n,s[maxn],op[maxn];
int L[maxn],R[maxn];
LL val[maxn];
vector<LL> ans;
inline void _read(int &x){
char ch=getchar(); bool mark=false;
for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
if(mark)x=-x;
}
int main(){
int i,j;
_read(n);
for(i=0;i<=n+1;i++)
L[i]=R[i]=i,val[i]=-1;
for(i=1;i<=n;i++)_read(s[i]);
for(i=1;i<=n;i++)_read(op[i]);
LL maxsum=0;
ans.push_back(0);
for(i=n;i>1;i--){
int cur=op[i];
LL x=s[cur];
val[cur]=s[cur];
if(val[cur-1]!=-1){
x+=val[cur-1];
L[R[cur]]=L[cur-1];
val[R[cur]]=x;
R[L[cur-1]]=R[cur];
val[L[cur-1]]=x;
}
if(val[cur+1]!=-1){
x+=val[cur+1];
R[L[cur]]=R[cur+1];
val[L[cur]]=x;
L[R[cur+1]]=L[cur];
val[R[cur+1]]=x;
}
maxsum=max(maxsum,x);
ans.push_back(maxsum);
}
for(i=ans.size()-1;i>=0;i--)
printf("%I64d ",ans[i]);
return 0;
}