D - Yet Another Yet Another Task 用線段樹寫水題

a[i]的數據範圍瞬間讓這題變成了水題  我們枚舉區間最大值 (從0到30) 負數沒必要 因爲當區間長度爲1時ans爲0 是最小值 我們只保留小於等於這個枚舉值的數 然後維護最小前綴和就行了 如果碰到大於枚舉值的位置 我們把最小前綴和設爲正無窮   

#include<stdio.h>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int ans,b[N],sum[N];
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
		sum[i]=sum[i-1]+b[i];
	}
	for(int i = 0; i <= 30; i++){
		int mn=2e9;
		for(int j = 1; j <= n; j++){
			if(b[j]>i){
				mn=2e9;
				continue;
			}
			mn=min(sum[j-1],mn);
			ans=max(ans,sum[j]-mn-i);
		}
	}
	printf("%d\n",ans);
}

不過如果a[i]沒有這麼小的範圍限制呢,我們顯然不能再去枚舉最大值了。我們只能遍歷數組,以每個位置爲最大值統計答案 。首先要用單調棧求出每個位置左右最靠近的比它大的位置 例如數組 9 3 4 6 7 那麼對於4來說 就是 位置1和位置5   用單調隊列正向反向分別遍歷就行  我們記爲lt[i]和rt[i]  那麼每個位置i的答案顯然就是  lt[i]~i的最大後綴和加上i~rt[i]的最大前綴和減去a[i]  這一部分我是用線段樹做的  用線段樹維護前綴和數組sum[]的最大最小值  顯然我們要求出lt[i]~i的最小前綴和以及i~rt[i]的最大前綴和 

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int a[N],sum[N],tp,sta[N],lt[N],rt[N];
struct seg{
	int l,r;
	int mx,mi;
}T[N<<2];
const int inf = 1e9;
#define mid (T[id].l+T[id].r>>1)
#define ls id<<1
#define rs ls|1
#define lson ls,l,mid
#define rson rs,mid+1,r
void pushup(int id){
	T[id].mx=max(T[ls].mx,T[rs].mx);
	T[id].mi=min(T[ls].mi,T[rs].mi); 
}
void build(int id,int l,int r){
	T[id].l=l,T[id].r=r;
	if(l==r){
		T[id].mx=T[id].mi=sum[l];
		return;
	}
	int m = l+r>>1;
	build(ls,l,m);build(rs,m+1,r);
	pushup(id);
}
int query(int id,int L,int R,int op){
	if(L<=T[id].l&&R>=T[id].r){
		return op?T[id].mx:T[id].mi; 
	}
	int ans;
	ans=op?-inf:inf;
	if(L<=mid) ans=(op?max(ans,query(ls,L,R,op)):min(ans,query(ls,L,R,op)));
	if(R>mid) ans=(op?max(ans,query(rs,L,R,op)):min(ans,query(rs,L,R,op)));
	return ans;
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i = 1; i <= n; i++){
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	build(1,0,n);
	a[0]=a[n+1]=inf;
	sta[++tp]=0;
	for(int i = 1; i <= n; i++){
		while(tp>=1&&a[sta[tp]]<=a[i]) tp--;
		lt[i]=sta[tp];
		sta[++tp]=i;
	}
	tp=0;
	sta[++tp]=n+1;
	for(int i = n; i >= 1; i--){
		while(tp>=1&&a[sta[tp]]<=a[i]) tp--;
		rt[i]=sta[tp];
		sta[++tp]=i;
	}
/*	for(int i = 1; i <= n; i++){
		printf("lt[%d]=%d rt[%d]=%d\n",i,lt[i],i,rt[i]);
	}
*/
	int ans = 0;
	for(int i = 1; i <= n; i++){
		//printf("Q1=%d Q2=%d sum[i]=%d ",query(1,lt[i],i-1,0),query(1,i,rt[i]-1,1),sum[i]);
		ans=max(ans,-query(1,lt[i],i-1,0)+query(1,i,rt[i]-1,1)-a[i]);
		//printf("ans=%d\n",ans);
	}
	printf("%d\n",ans);
	return 0;
}

 隊友(智傑巨巨)給了求後綴和最大值和前綴和最大值的方法,那就是拿線段樹維護。在求區間最大子串和的時候 如果用線段樹的話  我們都會維護最左最大子串和,最右最大子串和,區間最大子串和。那就可以直接從最左最大子串和,最右最大子串和下手了。一開始不知道怎麼寫query函數,後來智傑巨巨教我了。現在還是不很懂線段樹能維護什麼樣的區間屬性,看到過的是,這些屬性要具有區間可加性。這個怎麼說呢,如果能正確寫出pushup函數的,應該都可以維護。做的題多了,能維護的東西就那些,但是線段樹還是有很多很多匪夷所思的操作。還是得多學習。

#include<stdio.h>
#include<stack>
#include<algorithm>
using namespace std;
#define maxn 100010
int a[maxn];
int sum[maxn];
int x[maxn],y[maxn];
stack<int>s;
struct node{
	int l,r;
	int lmax,rmax;
};
node tree[400010];
void build(int k,int l,int r){
	tree[k].l=l;
	tree[k].r=r;
	if(l==r){
		tree[k].lmax=tree[k].rmax=a[l];
		return ;
	}
	int mid=(l+r)/2;
	build(2*k,l,mid);
	build(2*k+1,mid+1,r);
	tree[k].lmax=max(tree[2*k].lmax,sum[tree[2*k].r]-sum[tree[2*k].l-1]+tree[2*k+1].lmax);
	tree[k].rmax=max(tree[2*k+1].rmax,sum[tree[2*k+1].r]-sum[tree[2*k+1].l-1]+tree[2*k].rmax);
}
int query(int k,int l,int r,int flag){
	if(tree[k].l==l&&tree[k].r==r){
		if(flag==0)return tree[k].lmax;
		else return tree[k].rmax;
	}
	int mid=(tree[k].l+tree[k].r)/2;
	int ans;
	if(mid<r&&mid>=l){
		if(flag==0){
			ans=max(query(2*k,l,mid,flag),sum[mid]-sum[l-1]+query(2*k+1,mid+1,r,flag));
		}
		else{
			ans=max(query(2*k+1,mid+1,r,flag),sum[r]-sum[mid]+query(2*k,l,mid,flag));
		}
	}
	else if(mid>=r){
		ans=query(2*k,l,r,flag);
	}
	else{
		ans=query(2*k+1,l,r,flag);
	}
	return ans;
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	x[1]=1;
	s.push(1);
	for(int i=2;i<=n;i++){
		if(a[i]<=0)continue;
		while(!s.empty()&&a[s.top()]<=a[i]){
			s.pop();
		}
		if(s.empty()){
			x[i]=1;
		}
		else{
			x[i]=s.top()+1;
		}
		s.push(i);
	}
	while(!s.empty())s.pop();
	y[n]=n;
	s.push(n);
	for(int i=n-1;i>0;i--){
		if(a[i]<=0)continue;
		while(!s.empty()&&a[s.top()]<=a[i]){
			s.pop();
		}
		if(s.empty()){
			y[i]=n;
		}
		else{
			y[i]=s.top()-1;
		}
		s.push(i);
	}
	build(1,1,n);
	int ans=0;
	for(int i=1;i<=n;i++){
		if(a[i]<=0)continue;
		ans=max(query(1,x[i],i,1)+query(1,i,y[i],0)-2*a[i],ans);
	}
	printf("%d\n",ans);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章