码迷,mamicode.com
首页 > 其他好文 > 详细

SPOJ GSS 系列

时间:2018-07-22 11:27:11      阅读:133      评论:0      收藏:0      [点我收藏+]

标签:查询   get   style   code   print   div   scanf   string   type   

来怒做GSS系列了;

GSS1:https://www.luogu.org/problemnew/show/SP1043

这题就是维护一个 sum , mx , lmx , rmx,转移时用结构体就好了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int const maxn=50005;
int n,m,a[maxn];
struct N{
    int sum,mx,lmx,rmx;
}t[maxn<<2];
void pushup(int x)
{
    int ls=(x<<1),rs=(x<<1|1);
    t[x].sum=t[ls].sum+t[rs].sum;
    t[x].lmx=max(t[ls].lmx,t[ls].sum+t[rs].lmx);
    t[x].rmx=max(t[rs].rmx,t[rs].sum+t[ls].rmx);
    t[x].mx=max(t[x].sum,max(t[x].lmx,t[x].rmx));
    t[x].mx=max(t[x].mx,max(t[ls].mx,t[rs].mx));
    t[x].mx=max(t[x].mx,t[ls].rmx+t[rs].lmx);
}
N merge(N l,N r)
{
    N ret;
    ret.sum=l.sum+r.sum;
    ret.lmx=max(l.lmx,l.sum+r.lmx);
    ret.rmx=max(r.rmx,r.sum+l.rmx);
    ret.mx=max(ret.sum,max(ret.lmx,ret.rmx));
    ret.mx=max(ret.mx,max(l.mx,r.mx));
    ret.mx=max(ret.mx,l.rmx+r.lmx);
    return ret;
}
void build(int x,int l,int r)
{
    if(l==r){t[x].mx=t[x].lmx=t[x].rmx=t[x].sum=a[l]; return;}
    int mid=((l+r)>>1);
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    pushup(x);
}
N query(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return t[x];
    int mid=((l+r)>>1);
    if(mid<L)return query(x<<1|1,mid+1,r,L,R);
    else if(mid>=R)return query(x<<1,l,mid,L,R);
    else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",query(1,1,n,x,y).mx);
    }
    return 0;
}

 

GSS2:https://www.luogu.org/problemnew/show/SP1557

突然好难,不会做了...

看了半天TJ,还是云里雾里...可能是对最大连续子段和的知识太不熟悉了;

大概就是要离线,把询问按右端点排序,然后逐个加点;

因为要去重,所以新加入一个点,影响到的左端点的区间只在上一个这个值出现的位置之后,于是可以用线段树做;

线段树叶子节点上的值表示以这个点为左端点、当前节点(i)为右端点的各种值;

sum 是不断累加的和,his 是历史上的最大值,也是查询的答案;

因为是线段树,所以要考虑一下标记,区分成不断累加的标记 tag 和历史上的最大增量 htg;

pushdown 的转移有点复杂,要注意顺序;

还是感觉好难想(其实是套路?)好难写啊...

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=1e5+5;
int n,m,a[maxn],pre[maxn],lst[maxn<<1];
ll ans[maxn];
struct N{
    ll sum,his,tag,htg;
    N(){sum=his=tag=htg=0;}
}t[maxn<<2];
struct Q{int l,r,bh;}q[maxn];
bool cmp(Q x,Q y){return x.r<y.r;}
N merge(N l,N r)
{
    N ret;
    ret.sum=max(l.sum,r.sum);
    ret.his=max(l.his,r.his);
    return ret;
}
void pushup(int x){t[x]=merge(t[x<<1],t[x<<1|1]);}
void pushdown(int x)
{
    int ls=(x<<1),rs=(x<<1|1);
    t[ls].his=max(t[ls].his,t[ls].sum+t[x].htg);
    t[rs].his=max(t[rs].his,t[rs].sum+t[x].htg);
    t[ls].sum+=t[x].tag;
    t[rs].sum+=t[x].tag;
    t[ls].htg=max(t[ls].htg,t[ls].tag+t[x].htg);
    t[rs].htg=max(t[rs].htg,t[rs].tag+t[x].htg);
    t[ls].tag+=t[x].tag;
    t[rs].tag+=t[x].tag;
    t[x].tag=0; t[x].htg=0;
}
void insert(int x,int l,int r,int L,int R,int val)
{
    if(l>=L&&r<=R)
    {
        t[x].sum+=val;
        t[x].his=max(t[x].his,t[x].sum);
        t[x].tag+=val;
        t[x].htg=max(t[x].htg,t[x].tag);
        return;
    }
    pushdown(x);
    int mid=((l+r)>>1);
    if(mid>=L)insert(x<<1,l,mid,L,R,val);
    if(mid<R)insert(x<<1|1,mid+1,r,L,R,val);
    pushup(x);
}
N query(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return t[x];
    pushdown(x);
    int mid=((l+r)>>1);
    if(mid<L)return query(x<<1|1,mid+1,r,L,R);
    else if(mid>=R)return query(x<<1,l,mid,L,R);
    else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        pre[i]=lst[a[i]+maxn];
        lst[a[i]+maxn]=i;
    }
    scanf("%d",&m);
    for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].bh=i;
    sort(q+1,q+m+1,cmp);
    for(int i=1,j=1;i<=n;i++)
    {
        insert(1,1,n,pre[i]+1,i,a[i]);
        while(q[j].r<=i&&j<=m)ans[q[j].bh]=query(1,1,n,q[j].l,q[j].r).his,j++;
    }
    for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
    return 0;
}

 

GSS3:https://www.luogu.org/problemnew/show/SP1716

GSS1加修改。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int const maxn=50005;
int n,m,a[maxn];
struct N{
    int sum,mx,lmx,rmx;
}t[maxn<<2];
void pushup(int x)
{
    int ls=(x<<1),rs=(x<<1|1);
    t[x].sum=t[ls].sum+t[rs].sum;
    t[x].lmx=max(t[ls].lmx,t[ls].sum+t[rs].lmx);
    t[x].rmx=max(t[rs].rmx,t[rs].sum+t[ls].rmx);
    t[x].mx=max(t[x].sum,max(t[x].lmx,t[x].rmx));
    t[x].mx=max(t[x].mx,max(t[ls].mx,t[rs].mx));
    t[x].mx=max(t[x].mx,t[ls].rmx+t[rs].lmx);
}
N merge(N l,N r)
{
    N ret;
    ret.sum=l.sum+r.sum;
    ret.lmx=max(l.lmx,l.sum+r.lmx);
    ret.rmx=max(r.rmx,r.sum+l.rmx);
    ret.mx=max(ret.sum,max(ret.lmx,ret.rmx));
    ret.mx=max(ret.mx,max(l.mx,r.mx));
    ret.mx=max(ret.mx,l.rmx+r.lmx);
    return ret;
}
void build(int x,int l,int r)
{
    if(l==r){t[x].mx=t[x].lmx=t[x].rmx=t[x].sum=a[l]; return;}
    int mid=((l+r)>>1);
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    pushup(x);
}
N query(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return t[x];
    int mid=((l+r)>>1);
    if(mid<L)return query(x<<1|1,mid+1,r,L,R);
    else if(mid>=R)return query(x<<1,l,mid,L,R);
    else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
}
void update(int x,int l,int r,int pos,int val)
{
    if(l==r){t[x].mx=t[x].lmx=t[x].rmx=t[x].sum=val; return;}
    int mid=((l+r)>>1);
    if(pos<=mid)update(x<<1,l,mid,pos,val);
    else update(x<<1|1,mid+1,r,pos,val);
    pushup(x);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(int i=1,op,x,y;i<=m;i++)
    {
        scanf("%d%d%d",&op,&x,&y);
        if(op==0)update(1,1,n,x,y);
        if(op==1)printf("%d\n",query(1,1,n,x,y).mx);
    }
    return 0;
}

 

GSS4:https://www.luogu.org/problemnew/show/SP2713

由于一个数开方几次就到1了,所以对于一个全是1的区间就不用去修改了。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int const maxn=1e5+5;
int n,m,u;
ll a[maxn],sum[maxn<<2];
void build(int x,int l,int r)
{
    if(l==r){sum[x]=a[l]; return;}
    int mid=((l+r)>>1);
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    sum[x]=sum[x<<1]+sum[x<<1|1];
}
void update(int x,int l,int r,int L,int R)
{
    if(sum[x]==r-l+1)return;
    if(l==r){sum[x]=sqrt(sum[x]); return;}
    int mid=((l+r)>>1);
    if(L<=mid)update(x<<1,l,mid,L,R);
    if(R>mid) update(x<<1|1,mid+1,r,L,R);
    sum[x]=sum[x<<1]+sum[x<<1|1];
}
ll query(int x,int l,int r,int L,int R)
{
    if(l>=L&&r<=R)return sum[x];
    int mid=((l+r)>>1); ll ret=0;
    if(mid>=L)ret+=query(x<<1,l,mid,L,R);
    if(mid<R)ret+=query(x<<1|1,mid+1,r,L,R);
    return ret;
}
int main()
{
    while(~scanf("%d",&n))
    {
        printf("Case #%d:\n",++u);
        for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
        build(1,1,n);
        scanf("%d",&m);
        for(int i=1,op,x,y;i<=m;i++)
        {
            scanf("%d%d%d",&op,&x,&y);
            if(x>y)swap(x,y);
            if(op==0)update(1,1,n,x,y);
            if(op==1)printf("%lld\n",query(1,1,n,x,y));
        }
        printf("\n");
    }
    return 0;
}

 

GSS5:https://www.luogu.org/problemnew/show/SP2916

分类讨论,不相交或相交;

相交分穿过 x2,或穿过 y1,或在 x2 和 y1 中间;

细节有点神奇,注释那里要注意一下,因为如果是 x1,x2,那么实际还是没有摆脱 x2,所以不行(?);

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long ll;
int const maxn=1e5+5,inf=1e9;
int T,n,m,a[maxn];
struct N{
    int sum,mx,lmx,rmx;
    N(){sum=mx=lmx=rmx=0;}
}t[maxn<<2];
void pushup(int x)
{
    int ls=(x<<1),rs=(x<<1|1);
    t[x].sum=t[ls].sum+t[rs].sum;
    t[x].lmx=max(t[ls].lmx,t[ls].sum+t[rs].lmx);
    t[x].rmx=max(t[rs].rmx,t[rs].sum+t[ls].rmx);
    t[x].mx=max(t[ls].rmx+t[rs].lmx,max(t[ls].mx,t[rs].mx));
}
N merge(N l,N r)
{
    N ret;
    ret.sum=l.sum+r.sum;
    ret.lmx=max(l.lmx,l.sum+r.lmx);
    ret.rmx=max(r.rmx,r.sum+l.rmx);
    ret.mx=max(l.rmx+r.lmx,max(l.mx,r.mx));
    return ret;
}
void build(int x,int l,int r)
{
    if(l==r){t[x].sum=t[x].mx=t[x].lmx=t[x].rmx=a[l]; return;}
    int mid=((l+r)>>1);
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    pushup(x);
}
N query(int x,int l,int r,int L,int R)
{
    N ret;
    if(L>R)return ret;
    if(l>=L&&r<=R)return t[x];
    int mid=((l+r)>>1);
    if(mid<L)return query(x<<1|1,mid+1,r,L,R);
    else if(mid>=R)return query(x<<1,l,mid,L,R);
    else return merge(query(x<<1,l,mid,L,R),query(x<<1|1,mid+1,r,L,R));
}
int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,1,n);
        scanf("%d",&m);
        for(int i=1,x1,y1,x2,y2;i<=m;i++)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            if(y1<x2)
            {
                N ls=query(1,1,n,x1,y1),rs=query(1,1,n,x2,y2),mid=query(1,1,n,y1+1,x2-1);
                printf("%d\n",ls.rmx+mid.sum+rs.lmx);
            }
            else
            {
                int ans=-inf;
                N ls=query(1,1,n,x1,x2-1),rs=query(1,1,n,x2,y2);//不能是 x1,x2 和 x2+1,y2? 
                ans=max(ans,ls.rmx+rs.lmx);
                ls=query(1,1,n,x1,y1),rs=query(1,1,n,y1+1,y2);
                ans=max(ans,ls.rmx+rs.lmx);
                ls=query(1,1,n,x2,y1);
                ans=max(ans,ls.mx);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

  (待更)

SPOJ GSS 系列

标签:查询   get   style   code   print   div   scanf   string   type   

原文地址:https://www.cnblogs.com/Zinn/p/9343106.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!