SPOJ GSS系列是一系列序列维护的问题。
大部分用线段树,Splay等可以解决。
1043. GSS1
题意:给定长度为N的数串,M个询问查询[a,b]的的最大连续子段和。
线段树每个节点维护左端最大连续和,右端最大连续和,中间最大连续和。
就可以快速合并了。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,a[50005];
int sum[50000*4],ls[50000*4],rs[50000*4],mx[50000*4];
inline void up(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
ls[o]=max(ls[o<<1],sum[o<<1]+ls[o<<1|1]);
rs[o]=max(rs[o<<1|1],sum[o<<1|1]+rs[o<<1]);
mx[o]=max(max(mx[o<<1],mx[o<<1|1]),ls[o<<1|1]+rs[o<<1]);
}
void bd(int o,int l,int r){
if(l==r){
mx[o]=ls[o]=rs[o]=sum[o]=a[l];
}else{
int mid=(l+r)/2;
bd(o<<1,l,mid);
bd(o<<1|1,mid+1,r);
up(o);
}
//printf("%d %d %d\n",l,r,mx[o]);
}
int _l,_r;
int lmx,mxx;
void ask(int o,int l,int r){
// printf("# %d %d %d\n",o,l,r);
if(_l<=l&&r<=_r){
mxx=max(mxx,max(mx[o],lmx+ls[o]));
lmx=max(0,max(sum[o]+lmx,rs[o]));
}else{
int mid=(l+r)>>1;
if(_l<=mid) ask(o<<1,l,mid);
if(_r>mid) ask(o<<1|1,mid+1,r);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
bd(1,1,n);
scanf("%d",&m);
for(int i=0,l,r;i<m;i++){
scanf("%d%d",&l,&r);
lmx=0;
mxx=-0x3f3f3f3f;
if(l>r) swap(l,r);
_l=l,_r=r;
ask(1,1,n);
printf("%d\n",mxx);
}
// while(1);
return 0;
}
1557. GSS2
题意 与上一题相同,只是相同的数只算一次。
离线做。。类似tyvj cpu监控。
将所有查询按右端点排序。
维护所有前缀的后缀。
即固定右端点时不同左端点的序列和。
设一个位置x的数字a[x]上一次出现的位置是pos[x].
考虑右端点从r变到r+1时,以r+1结尾的序列只是把pos[r+1]…r+1加上了a[r+1]。
维护每个点的当前最大值,历史最大值即可。
对于询问(l,r),(l,r)上的历史最大值即为答案。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,q,a[100005];
int pos[100005],last[200005];
const int zero=100000;
const int MAXN=100005*4;
int mx[MAXN],hmx[MAXN],tag[MAXN],htag[MAXN];
struct N{
int l,r,id;
friend bool operator < (const N&a,const N&b){
return a.r<b.r;
}
}Q[100005];
int ans[MAXN];
int _l,_r,_c;
void update(int o){
mx[o]+=_c;
hmx[o]=max(hmx[o],mx[o]);
tag[o]+=_c;
htag[o]=max(htag[o],tag[o]);
}
void pd(int o){
if(htag[o]){
hmx[o<<1]=max(hmx[o<<1],mx[o<<1]+htag[o]);
hmx[o<<1|1]=max(hmx[o<<1|1],mx[o<<1|1]+htag[o]);
htag[o<<1]=max(htag[o<<1],tag[o<<1]+htag[o]);
htag[o<<1|1]=max(htag[o<<1|1],tag[o<<1|1]+htag[o]);
htag[o]=0;
}
if(tag[o]){
mx[o<<1]+=tag[o];
mx[o<<1|1]+=tag[o];
tag[o<<1]+=tag[o];
tag[o<<1|1]+=tag[o];
tag[o]=0;
}
}
void up(int o){
hmx[o]=max(hmx[o<<1],hmx[o<<1|1]);
mx[o]=max(mx[o<<1],mx[o<<1|1]);
}
void add(int o,int l,int r){
if(_l<=l&&r<=_r){
update(o);
}else{
int mid=(l+r)>>1;
pd(o);
if(_l<=mid) add(o<<1,l,mid);
if(_r>mid) add(o<<1|1,mid+1,r);
up(o);
}
}
void ask(int o,int l,int r){
if(_l<=l&&r<=_r){
_c=max(_c,hmx[o]);
}else{
int mid=(l+r)>>1;
pd(o);
if(_l<=mid) ask(o<<1,l,mid);
if(_r>mid) ask(o<<1|1,mid+1,r);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
if(last[a[i]+zero]) pos[i]=last[a[i]+zero];
last[a[i]+zero]=i;
}
scanf("%d",&q);
for(int i=0;i<q;i++) scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].id=i;
sort(Q,Q+q);
int k=0;
for(int i=1;i<=n;i++){
_l=pos[i]+1,_r=i,_c=a[i];
add(1,1,n);
while(k<q&&Q[k].r==i){
_l=Q[k].l;
_c=0;
ask(1,1,n);
ans[Q[k].id]=_c;
k++;
}
}
for(int i=0;i<q;i++) printf("%d\n",ans[i]);
// while(1);
return 0;
}
1716.GSS3
题意:GSS1支持修改。
反正也写线段树了,那就修改吧。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,a[50005];
int sum[50000*4],ls[50000*4],rs[50000*4],mx[50000*4];
inline void up(int o){
sum[o]=sum[o<<1]+sum[o<<1|1];
ls[o]=max(ls[o<<1],sum[o<<1]+ls[o<<1|1]);
rs[o]=max(rs[o<<1|1],sum[o<<1|1]+rs[o<<1]);
mx[o]=max(max(mx[o<<1],mx[o<<1|1]),ls[o<<1|1]+rs[o<<1]);
}
void bd(int o,int l,int r){
if(l==r){
mx[o]=ls[o]=rs[o]=sum[o]=a[l];
}else{
int mid=(l+r)/2;
bd(o<<1,l,mid);
bd(o<<1|1,mid+1,r);
up(o);
}
}
int _l,_r;
int lmx,mxx;
void ask(int o,int l,int r){
if(_l<=l&&r<=_r){
mxx=max(mxx,max(mx[o],lmx+ls[o]));
lmx=max(0,max(sum[o]+lmx,rs[o]));
}else{
int mid=(l+r)>>1;
if(_l<=mid) ask(o<<1,l,mid);
if(_r>mid) ask(o<<1|1,mid+1,r);
}
}
void mo(int o,int l,int r,int p,int to){
if(l==r){
mx[o]=ls[o]=rs[o]=sum[o]=to;
}else{
int mid=(l+r)>>1;
if(p<=mid) mo(o<<1,l,mid,p,to);
else mo(o<<1|1,mid+1,r,p,to);
up(o);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
bd(1,1,n);
scanf("%d",&m);
for(int i=0,t,l,r;i<m;i++){
scanf("%d%d%d",&t,&l,&r);
if(t&1){
lmx=0;
mxx=-0x3f3f3f3f;
if(l>r) swap(l,r);
_l=l,_r=r;
ask(1,1,n);
printf("%d\n",mxx);
}else{
mo(1,1,n,l,r);
}
}
// while(1);
return 0;
}
2713.GSS4
题意:给你一个数列,需要完成区间求和,区间开平方。
开平方没什么性质。
只好暴力开了。
线段树维护一个tag。
当这个区间都开成1了就不用再开了。
就好了。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
int n;
LL a[100005];
LL sum[100005*4];
bool tag[100005*4];
int _l,_r;LL _c;
void bd(int o,int l,int r){
if(l==r){
sum[o]=a[l];
if(sum[o]<=1) tag[o]=1;
}else{
int mid=(l+r)/2;
bd(o<<1,l,mid);bd(o<<1|1,mid+1,r);
tag[o]=tag[o<<1]&tag[o<<1|1];
sum[o]=sum[o<<1]+sum[o<<1|1];
}
}
void ask(int o,int l,int r){
if(_l<=l&&r<=_r){
_c+=sum[o];
}else{
int mid=(l+r)>>1;
if(_l<=mid) ask(o<<1,l,mid);
if(_r>mid) ask(o<<1|1,mid+1,r);
}
}
void mo(int o,int l,int r){
if(tag[o]) return;
if(_l<=l&&r<=_r){
if(l==r){
sum[o]=sqrt(sum[o]);
if(sum[o]<=1) tag[o]=1;
}else{
int mid=(l+r)>>1;
mo(o<<1,l,mid);
mo(o<<1|1,mid+1,r);
tag[o]=tag[o<<1]&tag[o<<1|1];
sum[o]=sum[o<<1]+sum[o<<1|1];
}
}else{
int mid=(l+r)>>1;
if(_l<=mid) mo(o<<1,l,mid);
if(_r>mid) mo(o<<1|1,mid+1,r);
tag[o]=tag[o<<1]&tag[o<<1|1];
sum[o]=sum[o<<1]+sum[o<<1|1];
}
}
//#define lld I64d
int kase;
int main(){
while(~scanf("%d",&n)){
printf("Case #%d:\n",++kase);
memset(tag,0,sizeof tag);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
bd(1,1,n);
int m,t,l,r;
scanf("%d",&m);
while(m--){
scanf("%d%d%d",&t,&l,&r);
if(l>r) swap(l,r);
if(t&1){
_c=0;
_l=l,_r=r;
ask(1,1,n);
printf("%lld\n",_c);
}else{
_l=l,_r=r;
mo(1,1,n);
}
}
puts("");
}
return 0;
}
2916.GSS5
题意:在GSS1的基础上。
增加了询问需要满足l在[x1,y1]之间,r在[x2,y2]之间。
好像讨论就可以了:
待填。