GSS系列

SPOJ 的数据结构的专题训练,没事写一下题解。

洛谷现成的题单

GSS1 - Can you answer these queries I

没有修改哦,考虑如何求最大子段和。

可以想到大区间的最大子段和可以分为三种情况,只在左只在右在中间横跨左右区间,在中间的怎么求呢,可以发现区间和可以是左区间的最大后缀和右区间的最大前缀和和,接下来取个最大值就好了。

image

那么我们就要想如何求最大前缀和了,大区间的最大前缀和的右端点可以不超过中间也可以超过中间,前者直接用左儿子区间的最大前缀和,后者用左区间总和加上右区间的最大前缀和即可,此时取最大值赋值即可,最大后缀和同样方法求就可以了。

image

此时我们明确了要维护区间的最大子段和、最大前缀和、最大后缀和、区间总和,然后 do it!!!。

我相信你可以的
#include<bits/stdc++.h>
#define ll long long 
#define ls (p<<1)
#define rs (p<<1|1)
using namespace std;
#define int ll
const int N=3e5+10;
int n,m,q;
int w[N];

inline int read(){
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}

struct ss{
	int sum,lmax,rmax,mx;
}a[N];

void pushup(int p){
	a[p].lmax=max(a[ls].lmax,a[ls].sum+a[rs].lmax);
	a[p].rmax=max(a[rs].rmax,a[rs].sum+a[ls].rmax);
	a[p].sum=a[ls].sum+a[rs].sum;
	a[p].mx=max(a[ls].rmax+a[rs].lmax,max(a[ls].mx,a[rs].mx));
}

void build(int p,int l,int r){
	if(l==r){
		a[p].lmax=a[p].rmax=a[p].sum=a[p].mx=w[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid); 
	build(rs,mid+1,r);
	pushup(p);
}

ss query(int p,int pl,int pr,int l,int r){
	if(pl>=l&&pr<=r){
		return a[p];
	}
	int mid=(pl+pr)>>1;
	if(l>mid){
		return query(rs,mid+1,pr,l,r);
	}
	if(r<=mid){
		return query(ls,pl,mid,l,r);
	}
	ss ans,x,y;

	x=query(ls,pl,mid,l,r);
	y=query(rs,mid+1,pr,l,r);

	ans.lmax=max(x.lmax,x.sum+y.lmax);
	ans.rmax=max(y.rmax,y.sum+x.rmax);
	ans.sum=x.sum+y.sum;
	ans.mx=max(x.rmax+y.lmax,max(x.mx,y.mx));
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);
	n=read();
	for(int i=1;i<=n;i++){
		w[i]=read();
	}
	build(1,1,n);
	m=read();
	for(int i=1;i<=m;i++){
		int l,r;
		l=read(),r=read();
		cout<<query(1,1,n,l,r).mx<<"\n";
	}
	return 0;
}

GSS3 - Can you answer these queries III

这就是 GSS1 加了个修改操作没什么好说的,就简单加上就可以了。

这你也要看???我看你真是饿了
#include<bits/stdc++.h>
#define ll long long 
#define ls (p<<1)
#define rs (p<<1|1)
using namespace std;
#define int ll
const int N=5e5+10;
int n,m,q;
int w[N];

inline int read(){
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}

struct ss{
	int sum,lmax,rmax,mx;
}a[N];

void pushup(int p){
	a[p].lmax=max(a[ls].lmax,a[ls].sum+a[rs].lmax);
	a[p].rmax=max(a[rs].rmax,a[rs].sum+a[ls].rmax);
	a[p].sum=a[ls].sum+a[rs].sum;
	a[p].mx=max(a[ls].rmax+a[rs].lmax,max(a[ls].mx,a[rs].mx));
}
void build(int p,int l,int r){
	if(l==r){
		a[p].lmax=a[p].rmax=a[p].sum=a[p].mx=w[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid); 
	build(rs,mid+1,r);
	pushup(p);
}

void change(int p,int pl,int pr,int pos,int k){
	if(pl==pr){
		a[p].lmax=a[p].rmax=a[p].sum=a[p].mx=k;
		return;
	} 
	int mid=(pl+pr)>>1;
	if(pos<=mid){
		change(ls,pl,mid,pos,k); 
	}
	else{
		change(rs,mid+1,pr,pos,k);
	}
	pushup(p);
}
ss query(int p,int pl,int pr,int l,int r){
	if(pl>=l&&pr<=r){
		return a[p];
	}
	int mid=(pl+pr)>>1;
	if(l>mid){
		return query(rs,mid+1,pr,l,r);
	}
	if(r<=mid){
		return query(ls,pl,mid,l,r);
	}
	ss ans,x,y;
		 
	x=query(ls,pl,mid,l,r);
	y=query(rs,mid+1,pr,l,r);
		
	ans.lmax=max(x.lmax,x.sum+y.lmax);
	ans.rmax=max(y.rmax,y.sum+x.rmax);
	ans.sum=x.sum+y.sum;
	ans.mx=max(x.rmax+y.lmax,max(x.mx,y.mx));
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);
	n=read();
	for(int i=1;i<=n;i++){
		w[i]=read();
	}
	build(1,1,n);
	m=read();
	for(int i=1;i<=m;i++){
		int op,l,r;
		op=read(),l=read(),r=read();
		if(op==0){
			change(1,1,n,l,r);	
		}
		else{
			cout<<query(1,1,n,l,r).mx<<"\n";
		}
	}
	return 0;
} 

GSS4 - Can you answer these queries IV

某某双倍经验,因为开方有个性质,开方大约6次后就必定为1,当区间最大值不大于1时,那这次操作就没有意义,不执行操作,有了这个剪枝后你就可以直接暴力修改了。

这么简单的思路也要看代码???
#include<bits/stdc++.h>
#define ll long long 
#define ls (p<<1)
#define rs (p<<1|1)
using namespace std;
#define int ll
const int N=5e5+10;
int n,m,q;
int w[N];
int sum[N],mx[N];

inline int read(){
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
	return x*f;
}

void build(int p,int l,int r){
	if(l==r){
		sum[p]=w[l];
		mx[p]=w[l];
		return;
	}
	int mid=(l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	sum[p]=sum[ls]+sum[rs];
	mx[p]=max(mx[ls],mx[rs]);
}

void change(int p,int pl,int pr,int l,int r){
	if(mx[p]<=1){
		return;
	}
	if(pl==pr){
		sum[p]=sqrt(sum[p]);
		mx[p]=sqrt(mx[p]);
		return;
	} 
	int mid=(pl+pr)>>1; 
	if(l<=mid&&mx[ls]>1){
		change(ls,pl,mid,l,r);
	}
	if(mid<r&&mx[rs]>1){
		change(rs,mid+1,pr,l,r);
	}
	sum[p]=sum[ls]+sum[rs];
	mx[p]=max(mx[ls],mx[rs]);
	if(mx[p]<=1){
		return;
	}
}

int query(int p,int pl,int pr,int l,int r){
	int ans=0;
	if(pl>r||pr<l){
		return 0;
	}
	if(pl>=l&&pr<=r){
		return sum[p];
	} 
	int mid=(pl+pr)>>1; 
	ans+=query(ls,pl,mid,l,r);
	ans+=query(rs,mid+1,pr,l,r);
	return ans;
}

signed main(){
//	ios::sync_with_stdio(false);
	int cnt=0;
	while((scanf("%lld",&n))!=EOF){
		printf("Case #%d:\n",++cnt);
		for(int i=1;i<=n;i++){
			w[i]=read();
		}
		build(1,1,n); 
		cin>>m;
		for(int i=1;i<=m;i++){
			int op;
			int u,v;
			op=read();
			u=read();
			v=read();
			if(u>v){
				swap(u,v);
			}
			if(op==1){
				printf("%lld\n",query(1,1,n,u,v));
			}
			else{
				change(1,1,n,u,v);
			}
		}
		printf("\n");
		memset(sum,0,sizeof sum);
		memset(mx,0,sizeof mx);
	}
	return 0;
} 

posted @ 2024-10-09 08:44  sad_lin  阅读(5)  评论(0编辑  收藏  举报