BZOJ2482: [Spoj1557] Can you answer these queries II

没睡醒的时候做这个题是最致命的  看上去能做 但是没有仔细分析就会掉进坑里 言归正传

题解: 这个题 很明显离线做 对于每个位置由线段树维护以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
",ans1[i]);
	return 0;
}

2482: [Spoj1557] Can you answer these queries II

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 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


Sample Output


4
5
3

HINT

【数据说明】

30%:1 <= n, m <= 100

100%:1 <= n, m <= 100000

原文地址:https://www.cnblogs.com/wang9897/p/9477537.html