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

SPOJ GSS

时间:2018-02-12 22:20:15      阅读:170      评论:0      收藏:0      [点我收藏+]

标签:signed   部分   支持   hang   交集   变化   let   insert   using   

GSS1

  题目大意:给出一个数列,多次询问区间最长连续子段和
  题解:线段树维护区间最长连续子段和gss,区间从最左元素开始的最长连续子段和lgss
  区间以最右元素为结尾的最长连续子段和rgss以及区间和s,信息传递并合并即可

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100010,M=N<<2;
struct data{int s,lgss,rgss,gss;}T[M];
int a[N],n,m;
data merge(data L,data R){
    data res;
    res.s=L.s+R.s;
    res.lgss=max(L.lgss,L.s+R.lgss);
    res.rgss=max(L.rgss+R.s,R.rgss);
    res.gss=max(max(L.gss,R.gss),L.rgss+R.lgss);
    return res;
}
void build(int x,int l,int r){
    if(l==r){T[x].s=T[x].lgss=T[x].rgss=T[x].gss=a[l];return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    T[x]=merge(T[x<<1],T[x<<1|1]);
}
data query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R)return T[x];
    int mid=(l+r)>>1;
    data lft,rht;
    if(L<=mid)lft=query(x<<1,l,mid,L,R);
    if(R>mid)rht=query(x<<1|1,mid+1,r,L,R);
    if(R<=mid)return lft;
    if(L>mid)return rht;
    return merge(lft,rht);
}
int main(){
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,1,n);
        scanf("%d",&m);
        while(m--){
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d\n",query(1,1,n,l,r).gss);
        }
    }return 0;
}

GSS2

  题目大意:给出一个数列,多次询问区间最大连续子段和,计数时重复元素只算进子段和中一次
  题解:考虑离线对询问进行扫描线,线段树i位置表示i时间一直到当前的时间节点的累加值,
  区间最大值nmx表示当前时间为终点的后缀最大子段和,记mx为区间最大子段和,
  我们发现区间最大子段和mx其实就是后缀最大子段和的历史最大值,
  每次处理到节点i,只要在区间加上a[i],就能更新所有的后缀和nmx,
  对于非重元素,更新区间为[1,i],对于重复元素,更新区间为[lst[a[i]]+1,i],
  区间更新需要打tag,相对的,我们还要维护一个历史最大标记mxtag,
  历史最大值的是需要当前的最大值加上下传的历史最大标记来更新的,
  在历史最大值更新之后才能用tag去更新当前的最大值,
  因为线段树标记发生变化的前提条件是上方没有任何标记,
  所以上下标记作用的时间域不会有交集,从而能保证正确性。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int N=100010,M=N<<2; 
namespace Segment_Tree{
    int tot;
    struct node{int l,r,a,b;LL tag,mx,mxtag,nmx;}T[M];
    void build(int,int);
    void Initialize(int n){
        tot=0;
        build(1,n);
    } 
    void pb(int x){
        T[T[x].l].mxtag=max(T[T[x].l].mxtag,T[T[x].l].tag+T[x].mxtag);
        T[T[x].r].mxtag=max(T[T[x].r].mxtag,T[T[x].r].tag+T[x].mxtag);
        T[T[x].l].tag+=T[x].tag; T[T[x].r].tag+=T[x].tag;
        T[T[x].l].mx=max(T[T[x].l].mx,T[T[x].l].nmx+T[x].mxtag);
        T[T[x].r].mx=max(T[T[x].r].mx,T[T[x].r].nmx+T[x].mxtag);
        T[T[x].l].nmx+=T[x].tag; T[T[x].r].nmx+=T[x].tag;
        T[x].tag=0; T[x].mxtag=-INF;
    }
    void up(int x){
        T[x].mx=max(T[T[x].l].mx,T[T[x].r].mx);
        T[x].nmx=max(T[T[x].l].nmx,T[T[x].r].nmx);
    }
    void build(int l,int r){
        int x=++tot;
        T[x].a=l;T[x].b=r;T[x].l=T[x].r=T[x].tag=0;
        T[x].mxtag=T[x].nmx=T[x].mx=-INF;
        if(l==r){return;}
        int mid=(l+r)>>1;
        T[x].l=tot+1;build(l,mid);
        T[x].r=tot+1;build(mid+1,r);
    }
    void modify(int x,int y,int p){
        if(T[x].a==T[x].b){T[x].mx=T[x].nmx=p;return;}
        int mid=(T[x].a+T[x].b)>>1; pb(x);
        if(mid>=y)modify(T[x].l,y,p);
        if(mid<y)modify(T[x].r,y,p);
        up(x);
    }
    void change(int x,int a,int b,int p){
        if(T[x].a>=a&&T[x].b<=b){
            T[x].nmx+=p;
            T[x].mx=max(T[x].mx,T[x].nmx);
            T[x].tag+=p;
            T[x].mxtag=max(T[x].mxtag,T[x].tag);
            return;
        }
        int mid=(T[x].a+T[x].b)>>1; pb(x);
        if(mid>=a)change(T[x].l,a,b,p);
        if(mid<b)change(T[x].r,a,b,p);
        up(x);
    }
    LL query(int x,int a,int b){
        if(T[x].a>=a&&T[x].b<=b)return T[x].mx;
        int mid=(T[x].a+T[x].b)>>1; pb(x);
        LL res=-INF;
        if(mid>=a)res=max(res,query(T[x].l,a,b));
        if(mid<b)res=max(res,query(T[x].r,a,b)); 
        return res;
    } 
}
const int base=100000;
LL ans[N];
int head[N],nxt[N],v[N],lst[N+base];
void add(int x,int y,int id){
    nxt[id]=head[y]; head[y]=id; v[id]=x;
}
int n,m,a[N];
int main(){
    while(~scanf("%d",&n)){
        memset(head,0,sizeof(head));
        memset(lst,0,sizeof(lst));
        Segment_Tree::Initialize(n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        scanf("%d",&m);
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y,i);
        }
        for(int i=1;i<=n;i++){
        	Segment_Tree::modify(1,i,a[i]);
        	if(lst[a[i]+base]+1<i)Segment_Tree::change(1,lst[a[i]+base]+1,i-1,a[i]);
            lst[a[i]+base]=i;
            for(int j=head[i];j;j=nxt[j])ans[j]=Segment_Tree::query(1,v[j],i);
        }
        for(int i=1;i<=m;i++)printf("%lld\n",max(ans[i],0ll));
    }return 0;
}

GSS3

  题目大意:给出一个数列,多次询问区间最长连续子段和,支持单点修改
  题解:线段树维护区间最长连续子段和gss,区间从最左元素开始的最长连续子段和lgss
  区间以最右元素为结尾的最长连续子段和rgss以及区间和s,信息传递并合并即可
  单点修改最多影响树上log个点,重新合并计算即可

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100010,M=N<<2;
struct data{int s,lgss,rgss,gss;}T[M];
int a[N],n,m;
data merge(data L,data R){
    data res;
    res.s=L.s+R.s;
    res.lgss=max(L.lgss,L.s+R.lgss);
    res.rgss=max(L.rgss+R.s,R.rgss);
    res.gss=max(max(L.gss,R.gss),L.rgss+R.lgss);
    return res;
}
void build(int x,int l,int r){
    if(l==r){T[x].s=T[x].lgss=T[x].rgss=T[x].gss=a[l];return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    T[x]=merge(T[x<<1],T[x<<1|1]);
}
void change(int x,int l,int r,int y,int p){
    if(l==r){T[x].s=T[x].lgss=T[x].rgss=T[x].gss=p;return;}
    int mid=(l+r)>>1;
    if(y<=mid)change(x<<1,l,mid,y,p); 
    else change(x<<1|1,mid+1,r,y,p);
    T[x]=merge(T[x<<1],T[x<<1|1]);
}
data query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R)return T[x];
    int mid=(l+r)>>1;
    data lft,rht;
    if(L<=mid)lft=query(x<<1,l,mid,L,R);
    if(R>mid)rht=query(x<<1|1,mid+1,r,L,R);
    if(R<=mid)return lft;
    if(L>mid)return rht;
    return merge(lft,rht);
}
int main(){
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        build(1,1,n);
        scanf("%d",&m);
        while(m--){
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            if(op==0)change(1,1,n,l,r);
            else printf("%d\n",query(1,1,n,l,r).gss);
        }
    }return 0;
}

GSS4

  题目大意:维护一个数列,支持区间开方和区间求和
  题解:我们记录区间最大值,暴力开方,因为开方到1之后无需再开方,
  因此当区间最大值为1时该区间不再递归处理

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=1000010,M=2000010;
namespace Segment_Tree{
    int tot;
    struct node{int l,r,a,b;LL mx,val;}T[M];
    void build(int,int);
    void Initialize(int n){
        tot=0;
        build(1,n);
    } 
    void up(int x){
        T[x].val=T[T[x].l].val+T[T[x].r].val;
        T[x].mx=max(T[T[x].l].mx,T[T[x].r].mx);
    }
    void build(int l,int r){
        int x=++tot;
        T[x].a=l;T[x].b=r;T[x].l=T[x].r=T[x].val=T[x].mx=0;
        if(l==r){scanf("%lld",&T[x].val);T[x].mx=T[x].val;return;}
        int mid=(l+r)>>1;
        T[x].l=tot+1;build(l,mid);
        T[x].r=tot+1;build(mid+1,r);
        up(x);
    }
    void change(int x,int a,int b){
        if(T[x].mx==1)return;
        if(T[x].a==T[x].b){T[x].mx=T[x].val=sqrt(T[x].val);return;}
        int mid=(T[x].a+T[x].b)>>1;
        if(mid>=a&&T[x].l)change(T[x].l,a,b);
        if(mid<b&&T[x].r)change(T[x].r,a,b);
        up(x);
    }
    LL query(int x,int a,int b){
        if(T[x].a>=a&&T[x].b<=b)return T[x].val;
        int mid=(T[x].a+T[x].b)>>1; LL res=0;
        if(mid>=a&&T[x].l)res+=query(T[x].l,a,b);
        if(mid<b&&T[x].r)res+=query(T[x].r,a,b); 
        return res;
    } 
}
int n,m,t,l,r,cnt=0;
int main(){
    while(~scanf("%d",&n)){
    	Segment_Tree::Initialize(n);
    	if(cnt)puts("");
    	printf("Case #%d:\n",++cnt);
    	scanf("%d",&m);
    	while(m--){
            scanf("%d%d%d",&t,&l,&r);
            if(l>r)swap(l,r);
            if(t==0)Segment_Tree::change(1,l,r);
            else printf("%lld\n",Segment_Tree::query(1,l,r));
        }
    }return 0;
}

GSS5

  题目大意:给出一个数列,多次询问区间最长连续子段和,
  要求区间的左端点在x1,y1之间,右端点在x2,y2之间
  题解:线段树维护区间最长连续子段和gss,区间从最左元素开始的最长连续子段和lgss
  区间以最右元素为结尾的最长连续子段和rgss以及区间和s,信息传递并合并即可
  考虑询问区间的特殊性,如果两个端点区间不相交,
  则答案为[x1,y1].rgss+[y1+1,x2-1].s+[x2,y2].lgss
  如果区间相交,则中间区间[x2,y1]中必须有取到的部分,剩余两个区间可取可不取,
  但是必须与中间取到部分连接才能用于更新答案

#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100010,M=N<<2,INF=0x3f3f3f3f;
struct data{int s,lgss,rgss,gss;}T[M];
int a[N],s[N],n,m;
data merge(data L,data R){
    data res;
    res.s=L.s+R.s;
    res.lgss=max(L.lgss,L.s+R.lgss);
    res.rgss=max(L.rgss+R.s,R.rgss);
    res.gss=max(max(L.gss,R.gss),L.rgss+R.lgss);
    return res;
}
void build(int x,int l,int r){
    if(l==r){T[x].s=T[x].lgss=T[x].rgss=T[x].gss=a[l];return;}
    int mid=(l+r)>>1;
    build(x<<1,l,mid); build(x<<1|1,mid+1,r);
    T[x]=merge(T[x<<1],T[x<<1|1]);
}
data query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R)return T[x];
    int mid=(l+r)>>1;
    data lft,rht;
    if(L<=mid)lft=query(x<<1,l,mid,L,R);
    if(R>mid)rht=query(x<<1|1,mid+1,r,L,R);
    if(R<=mid)return lft;
    if(L>mid)return rht;
    return merge(lft,rht);
}
int Cas,ans;
data Q;
int main(){
    scanf("%d",&Cas);
    while(Cas--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
        build(1,1,n);
        scanf("%d",&m);
        while(m--){
            int x1,x2,y1,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            if(y1<x2){  // 区间不相交的情况
                ans=query(1,1,n,x1,y1).rgss;
                ans+=query(1,1,n,x2,y2).lgss;
                printf("%d\n",ans+s[x2-1]-s[y1]);
            }else{  // 区间相交
                ans=-INF;
                int L=0,M=0,MR=0,R=0;
                if(x2>x1){
                    Q=query(1,1,n,x1,x2-1);
                    L=max(Q.rgss,L);
                }
                Q=query(1,1,n,x2,y1);
                ans=max(L+Q.lgss,Q.gss);
                MR=Q.rgss; M=Q.s;
                if(y1<y2){
                    Q=query(1,1,n,y1+1,y2);
                    R=max(R,Q.lgss);
                }
                ans=max(ans,L+M+R);
                ans=max(ans,MR+R);
                printf("%d\n",ans);
            }
        }
    }return 0;
}

GSS6

  题目大意:维护一个数列,支持在x-1和x位置之间插入数字y,删除x位置上的数字,
  改变x位置上的数字以及询问区间最大子段和
  题解:splay维护区间最长连续子段和gss,区间从最左元素开始的最长连续子段和lgss
  区间以最右元素为结尾的最长连续子段rgss和以及区间和s

#include <cstdio>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=200010; 
namespace Splay{
    int a[N]; //原数列
    int val[N],s[N],lgss[N],rgss[N],gss[N],size[N],son[N][2],f[N],tot,root;
    int build(int,int,int);
    void Initialize(int n){tot=0;root=build(0,n+1,0);} 
    void up(int x){
        int ls=son[x][0],rs=son[x][1];
        size[x]=size[ls]+size[rs]+1;
        s[x]=s[ls]+s[rs]+val[x];
        lgss[x]=max(lgss[ls],s[ls]+val[x]+max(lgss[rs],0));
        rgss[x]=max(rgss[rs],s[rs]+val[x]+max(rgss[ls],0));
        gss[x]=val[x]+max(max(rgss[ls],lgss[rs]),max(0,lgss[rs]+rgss[ls]));
        gss[x]=max(gss[x],max(gss[ls],gss[rs]));
    }
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;
            if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];son[x][w^1]=y;f[y]=x;up(y);
    }
    void splay(int x,int w){
        int s=1,i=x,y;a[1]=x;
        while(f[i])a[++s]=i=f[i];
        while(f[x]!=w){
            y=f[x];
            if(f[y]!=w){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
            rotate(x);
        }if(!w)root=x;
        up(x);
    }
    // root=build(0,n+1,0);
    int build(int l,int r,int fa){
        int x=++tot,mid=(l+r)>>1;
        f[x]=fa;val[x]=a[mid];
        if(l<mid)son[x][0]=build(l,mid-1,x);
        if(r>mid)son[x][1]=build(mid+1,r,x);
        up(x);
        return x;
    }
    int kth(int k){
        int x=root,tmp;
        while(1){
            tmp=size[son[x][0]]+1;
            if(k==tmp)return x;
            if(k<tmp)x=son[x][0];else k-=tmp,x=son[x][1];
        }
    }
    // 删去第x个数字
    void DELETE(int x){
        int y=x;
        x=kth(x),y=kth(y+2);
        splay(x,0),splay(y,x),son[y][0]=0;
        up(y),up(x);
    }
    // 在第x-1个数字后面插入y
    void INSERT(int x,int y){
        x=kth(x);
        splay(x,0);
        f[++tot]=x,val[tot]=y;
        son[tot][1]=son[x][1],f[son[x][1]]=tot,son[x][1]=tot;
        up(tot),up(x);
    }
    // 替换第x个数字
    void REPLACE(int x,int y){
        x=kth(x+1);
        splay(x,0);
        val[x]=y;
        up(x);
    }
    // 查询区间最大子段和
    void QUERY(int x,int y){
        x=kth(x),y=kth(y+2);
        splay(x,0),splay(y,x);
        printf("%d\n",gss[son[y][0]]);
    } 
}
using namespace Splay;
int n,m;
int main(){
    scanf("%d",&n);
    // up里面数组太复杂了,没有判定子节点是否存在,所以要对0节点赋值-INF
    gss[0]=lgss[0]=rgss[0]=-INF;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    Initialize(n); 
    scanf("%d",&m);
    while(m--){
        char op[10]; int x,y;
        scanf("%s%d",op,&x);
        if(op[0]==‘D‘)DELETE(x);
        else scanf("%d",&y);
        if(op[0]==‘I‘)INSERT(x,y);
        if(op[0]==‘R‘)REPLACE(x,y);
        if(op[0]==‘Q‘)QUERY(x,y);
    }return 0;
}

GSS7

  题目大意:维护一棵树,允许链上修改,询问链上最长连续子链和
  题解:我们用动态树维护操作,对于链上修改,我们提取链后打标记即可,
  考虑到标记修改值绝对值在10000以内,我们用0x3f来标记累加值,
  维护区间最长连续子段和gss,区间从最左元素开始的最长连续子段和lgss
  区间以最右元素为结尾的最长连续子段和rgss以及区间和s,
  当标记传递时,lgss和rgss以及gss将会变为0或者区间长度乘新值,s正常维护即可。
  需要注意的是,当发生区间翻转的时候,因为左右子树的交换,lgss和rgss的值也要发生交换

#include <cstdio>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=200010; 
namespace Link_Cut_Tree{
    int f[N],son[N][2],tmp[N],size[N],tag[N],val[N]; bool rev[N];
    int s[N],lgss[N],rgss[N],gss[N];
    bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
    void rev1(int x){
		if(!x)return;
		swap(son[x][0],son[x][1]);
		// 子树旋转代表这区间的左右最长连续子段和也发生交换 
		swap(lgss[x],rgss[x]);  
		rev[x]^=1;
	}
    void addtag(int x,int y){
        if(!x)return;
        lgss[x]=rgss[x]=gss[x]=y>0?y*size[x]:0;
        s[x]=y*size[x];
        tag[x]=val[x]=y;
    }
    void pb(int x){
        if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;
        if(tag[x]^INF)addtag(son[x][0],tag[x]),addtag(son[x][1],tag[x]),tag[x]=INF;
    }
    void up(int x){ 
        int ls=son[x][0],rs=son[x][1];
        size[x]=size[ls]+size[rs]+1;
        s[x]=s[ls]+s[rs]+val[x];
        lgss[x]=max(lgss[ls],s[ls]+val[x]+max(lgss[rs],0));
        rgss[x]=max(rgss[rs],s[rs]+val[x]+max(rgss[ls],0));
        gss[x]=val[x]+max(max(rgss[ls],lgss[rs]),max(0,lgss[rs]+rgss[ls]));
        gss[x]=max(gss[x],max(gss[ls],gss[rs]));
    }
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;
			else if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
    }
    void splay(int x){
        int s=1,i=x,y;tmp[1]=i;
        while(!isroot(i))tmp[++s]=i=f[i];
        while(s)pb(tmp[s--]);
        while(!isroot(x)){
            y=f[x]; 
            if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
            rotate(x);
        }up(x);
    }
    void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
    int root(int x){access(x);splay(x);while(son[x][0])x=son[x][0];return x;}
    void makeroot(int x){access(x);splay(x);rev1(x);}
    void link(int x,int y){makeroot(x);f[x]=y;access(x);}
    void split(int x,int y){makeroot(y);access(x);splay(x);}
}
int n,m,op,x,y,z;
using namespace Link_Cut_Tree;
int main(){
    while(~scanf("%d",&n)){
        for(int i=1;i<=n;i++){
            scanf("%d",&val[i]);
            rev[i]=son[i][0]=son[i][1]=f[i]=0;
            size[i]=1; tag[i]=INF;
            lgss[i]=rgss[i]=gss[i]=val[i]>0?val[i]:0;
            s[x]=val[x];
        }
        for(int i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            link(x,y);
        }
        scanf("%d",&m);
        while(m--){
            scanf("%d%d%d",&op,&x,&y);
            split(x,y);
            if(op==1)printf("%d\n",gss[x]);
            else{
                scanf("%d",&z);
                rgss[x]=lgss[x]=gss[x]=z>0?z*size[x]:0;
                val[x]=tag[x]=z; s[x]=z*size[x];
            }
        }
    }return 0;
}

GSS8

  题目大意:维护一个数列,支持数字的插入,删除和替换,
  以及查询[l,r]中a[i]*(i-l+1)^k的和,maxk=10,答案对2^32取模
  题解:利用splay可以维护数字的插入和删改,接下来考虑如何在splay上维护a[i]*(i-l+1)^k
  设d[x][i]表示子树在k=i时候的答案,我们发现对于左子树来说,
  d[ls][i]是直接累加到d[x][i]上的,同时d[x][i]还要加上(pos-l+1)^k*a[x],
  我们发现pos-l恰好是左子树的大小,所以子树根贡献为(size[ls]+1)^k*a[x]
  考虑右子树的贡献,对于单个节点x来说,答案是(size[ls[root]]+1+size[ls]+1)^k*a[x]
  对size[ls[root]]+1和size[ls]+1进行二项式拆分,
  发现右子树每个节点拆分后的项可以合并,合并项包括k=1~i的d数组,
  其对根节点x的值d[x][i]的贡献为d[rs][j]*C[i][j]*(size[ls]+1)^(k-j)
  对d数组进行维护即可,考虑到取模数字的特殊性,直接用uint自然溢出

#include <cstdio>
#include <algorithm>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=200010; 
const int K=10;
typedef unsigned int uint;
uint C[20][20],d[N][K+5];
namespace Splay{
    int a[N]; //原数列
    int val[N],size[N],son[N][2],f[N],tot,root;
    int build(int,int,int);
    void Initialize(int n){tot=0;root=build(0,n+1,0);} 
    void up(int x){
        int ls=son[x][0],rs=son[x][1];
        size[x]=size[ls]+size[rs]+1;
        int sz=size[ls]+1;
        uint sum=1;
        for(int i=0;i<=K;i++){
            d[x][i]=d[ls][i]+val[x]*sum;
            sum=sum*sz; 
        }
        for(int i=0;i<=K;i++){
            uint sum=1;
            for(int j=i;j>=0;sum=sum*sz,j--){
                d[x][i]+=d[rs][j]*sum*C[i][j];
            }
        }
    }
    void rotate(int x){
        int y=f[x],w=son[y][1]==x;
        son[y][w]=son[x][w^1];
        if(son[x][w^1])f[son[x][w^1]]=y;
        if(f[y]){
            int z=f[y];
            if(son[z][0]==y)son[z][0]=x;
            if(son[z][1]==y)son[z][1]=x;
        }f[x]=f[y];son[x][w^1]=y;f[y]=x;up(y);
    }
    void splay(int x,int w){
        int s=1,i=x,y;a[1]=x;
        while(f[i])a[++s]=i=f[i];
        while(f[x]!=w){
            y=f[x];
            if(f[y]!=w){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
            rotate(x);
        }if(!w)root=x;
        up(x);
    }
    // root=build(0,n+1,0);
    int build(int l,int r,int fa){
        int x=++tot,mid=(l+r)>>1;
        f[x]=fa;
        val[x]=a[mid];
		for(int i=0;i<=K;i++)d[x][i]=a[mid];
        if(l<mid)son[x][0]=build(l,mid-1,x);
        if(r>mid)son[x][1]=build(mid+1,r,x);
        up(x);
        return x;
    }
    int kth(int k){
        int x=root,tmp;
        while(1){
            tmp=size[son[x][0]]+1;
            if(k==tmp)return x;
            if(k<tmp)x=son[x][0];else k-=tmp,x=son[x][1];
        }
    }
    // 删去第x个数字
    void DELETE(int x){
        int y=x;
        x=kth(x),y=kth(y+2);
        splay(x,0),splay(y,x),son[y][0]=0;
        up(y),up(x);
    }
    // 在第x-1个数字后面插入y
    void INSERT(int x,int y){
        x=kth(x);
        splay(x,0);
        f[++tot]=x,val[tot]=y;
        for(int i=0;i<=K;i++)d[tot][i]=y;
        son[tot][1]=son[x][1],f[son[x][1]]=tot,son[x][1]=tot;
        up(tot),up(x);
    }
    // 替换第x个数字
    void REPLACE(int x,int y){
        x=kth(x+1);
        splay(x,0);
        val[x]=y;
        for(int i=0;i<=K;i++)d[x][i]=y;
        up(x);
    }
    // 查询[l,r]中a[i]*(i-l+1)^k的和
    void QUERY(int x,int y,int z){
        x=kth(x),y=kth(y+2);
        splay(x,0),splay(y,x);
        printf("%u\n",d[son[y][0]][z]);
    } 
}
using namespace Splay;
void initC(){
    C[0][0]=1;
    for(int i=1;i<=K;i++){
        C[i][0]=1;
        for(int j=1;j<=i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
    }
}
int n,m;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    initC(); 
    Initialize(n); 
    scanf("%d",&m);
    while(m--){
        char op[10]; int x,y,z;
        scanf("%s%d",op,&x); x++;
        if(op[0]==‘D‘)DELETE(x);
        else scanf("%d",&y);
        if(op[0]==‘I‘)INSERT(x,y);
        if(op[0]==‘R‘)REPLACE(x,y);
        if(op[0]==‘Q‘){
        	y++;
            scanf("%d",&z);
            QUERY(x,y,z);
        }
    }return 0;
}

  

 

SPOJ GSS

标签:signed   部分   支持   hang   交集   变化   let   insert   using   

原文地址:https://www.cnblogs.com/forever97/p/GSS.html

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