没睡醒的时候做这个题是最致命的 看上去能做 但是没有仔细分析就会掉进坑里 言归正传
题解: 这个题 很明显离线做 对于每个位置由线段树维护以x做起点[x,n]位置上的历史最大值 似乎吉利爷论文上有写 我们可以通过维护两个push操作直接的历史增量的峰值 来维护答案 我们可以这样分析 我们知道了rt的历史增量的峰值 那么rt<<1和rt<<1|1历史增量的峰值可以维护 然后我们可以通过增量维护历史最大值即可
#include <bits/stdc++.h>
const int MAXN=1e5+10;
#define ll long long
const ll inf=1e18;
using namespace std;
typedef struct node{
ll ans,sum,vtag,tag;
}node;
node d[MAXN<<2];
vector<int>vec;
int n,m;
int a[MAXN],b[MAXN];
void push(int rt){
d[rt<<1].ans=max(d[rt<<1].ans,d[rt<<1].sum+d[rt].vtag);
d[rt<<1|1].ans=max(d[rt<<1|1].ans,d[rt<<1|1].sum+d[rt].vtag);
d[rt<<1].sum+=d[rt].tag;d[rt<<1|1].sum+=d[rt].tag;
d[rt<<1].vtag=max(d[rt<<1].vtag,d[rt<<1].tag+d[rt].vtag);
d[rt<<1|1].vtag=max(d[rt<<1|1].vtag,d[rt<<1|1].tag+d[rt].vtag);
d[rt<<1].tag+=d[rt].tag;d[rt<<1|1].tag+=d[rt].tag;
d[rt].vtag=0;d[rt].tag=0;
}
void up(int x){
d[x].sum=max(d[x<<1].sum,d[x<<1|1].sum);
d[x].ans=max(d[x<<1].ans,d[x<<1|1].ans);
}
void update(int rt,int l,int r,int ql,int qr,int vul){
if(ql<=l&&r<=qr){
d[rt].sum+=vul;d[rt].tag+=vul;
d[rt].vtag=max(d[rt].vtag,d[rt].tag);
d[rt].ans=max(d[rt].ans,d[rt].sum);
return ;
}
int mid=(l+r)>>1;
push(rt);
if(ql<=mid)update(rt<<1,l,mid,ql,qr,vul);
if(qr>mid)update(rt<<1|1,mid+1,r,ql,qr,vul);
up(rt);
}
ll ans;
void querty(int rt,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr){ans=max(ans,d[rt].ans);return ;}
int mid=(l+r)>>1;
push(rt);
if(ql<=mid)querty(rt<<1,l,mid,ql,qr);
if(qr>mid)querty(rt<<1|1,mid+1,r,ql,qr);
up(rt);
}
typedef struct Node{
int l,r,id;
friend bool operator<(Node aa,Node bb){return aa.r<bb.r;}
}Node;
Node que[MAXN];int pre[MAXN],vis[MAXN];
ll ans1[MAXN];
int main(){
scanf("%d",&n);
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++)scanf("%d",&a[i]),vec.push_back(a[i]);
sort(vec.begin(),vec.end());
int sz=unique(vec.begin(),vec.end())-vec.begin();
for(int i=1;i<=n;i++)b[i]=lower_bound(vec.begin(),vec.begin()+sz,a[i])-vec.begin()+1;
for(int i=1;i<=n;i++){vis[i]=pre[b[i]]+1;pre[b[i]]=i;}
scanf("%d",&m);
for(int i=1;i<=m;i++)scanf("%d%d",&que[i].l,&que[i].r),que[i].id=i;
sort(que+1,que+m+1);int k=1;
for(int i=1;i<=m;i++){
while(k<=n&&que[i].r>=k){update(1,1,n,vis[k],k,a[k]);k++;}
//cout<<d[1].ans<<" "<<d[1].sum<<endl;
ans=0;querty(1,1,n,que[i].l,que[i].r);ans1[que[i].id]=ans;
}
for(int i=1;i<=m;i++)printf("%lld\n",ans1[i]);
return 0;
}
2482: [Spoj1557] Can you answer these queries II
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 280 Solved: 149
[Submit][Status][Discuss]
Description
给定n个元素的序列。
给出m个询问:求l[i]~r[i]的最大子段和(可选空子段)。
这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次。
比如:1,2,3,2,2,2出现了3次,但只算一次,于是这个序列的和是1+2+3=6。
Input
第一行一个数n。
第二行n个数,为给定的序列,这些数的绝对值小于等于100000。
第三行一个数m。
接下来m行,每行两个数,l[i],r[i]。
Output
M行,每行一个数,为每个询问的答案。
Sample Input
9
4 -2 -2 3 -1 -4 2 2 -6
3
1 2
1 5
4 9
4 -2 -2 3 -1 -4 2 2 -6
3
1 2
1 5
4 9
Sample Output
4
5
3
HINT
【数据说明】
30%:1 <= n, m <= 100
100%:1 <= n, m <= 100000