spoj GSS系列简要题解


传送门
这个 GSSGSSGSS系列全部是跟子段有关的数据结构菜题。
于是来水一篇博客。

GSS1

传送门
题意简述:求不带修的最大子段和。


思路:直接线段树走一发。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=5e4+5;
int n,a[N];
inline int max(const int&a,const int&b){return a>b?a:b;}
inline int max(const int&a,const int&b,const int&c){return a>b?(a>c?a:c):(b>c?b:c);}
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (T[p].l+T[p].r>>1)
	struct Val{
		int ls,rs,ms,sum;
		inline void init(const int&x){ls=rs=ms=sum=x;}
	};
	inline Val operator+(const Val&a,const Val&b){return (Val){max(a.ls,a.sum+b.ls),max(b.rs,b.sum+a.rs),max(a.ms,b.ms,a.rs+b.ls),a.sum+b.sum};}
	struct Node{int l,r;Val val;}T[N<<2];
	inline void pushup(int p){T[p].val=T[lc].val+T[rc].val;}
	inline void build(int p,int l,int r){
		T[p].l=l,T[p].r=r;
		if(l==r)return T[p].val.init(a[l]);
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
	inline Val query(int p,int ql,int qr){
		if(ql<=T[p].l&&T[p].r<=qr)return T[p].val;
		if(qr<=mid)return query(lc,ql,qr);
		if(ql>mid)return query(rc,ql,qr);
		return query(lc,ql,qr)+query(rc,ql,qr);
	}
}
inline int read(){
	int ans=0;
	bool f=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f^=1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return f?ans:-ans;
}
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	SGT::build(1,1,n);
	for(ri tt=read(),x;tt;--tt)x=read(),cout<<SGT::query(1,x,read()).ms<<'\n';
	return 0;
}

GSS2

传送门
唯一感觉有点思维难度的题。
题意简述:求不带修的最大子段和,对于一个子段里面同一个数值只能产生一次贡献。


思路:
考虑离线询问。
我们对于第iii次询问(l,r)(l,r)(l,r)把它扔进一个用vectorvectorvector储存的qryqryqry数组中,即qryr.push_back(l,i)qry_r.push\_back(l,i)qryr.push_back(l,i),然后我们从左往右加点。
当处理到这个询问的时候,相当于只用问对于所有下标在[l,r][l,r][l,r]之间的点,以它们为左端点开始向右延伸的最大子段的最大值是多少。
这个东西怎么维护?
我们用增量构造法,考虑把前面的在i−1i-1i1结尾的所有子段即[1,i−1],[2,i−1],...,[i−1,i−1][1,i-1],[2,i-1],...,[i-1,i-1][1,i1],[2,i1],...,[i1,i1]这些子段同时加上一个aia_iai去更新111~i−1i-1i1处的答案。
这样我们实际上对于一个点iii只需要维护两个东西:

  1. Sum(i,now)Sum(i,now)Sum(i,now),这个SumSumSum的求法是题意的求法,即相同的值只算一次。
  2. hisSum(i,now)=max(Sum(i,i),Sum(i,i+1),..,Sum(i,now))hisSum(i,now)=max(Sum(i,i),Sum(i,i+1),..,Sum(i,now))hisSum(i,now)=max(Sum(i,i),Sum(i,i+1),..,Sum(i,now))
    考虑每次加一个值aia_iai对于前面所有点的贡献。
    preipre_iprei表示上一个aia_iai出现的位置,那么对于那些下标在[1,prei][1,pre_i][1,prei]之间的点它们的SumSumSumhisSumhisSumhisSum是不变的(因为加上这个值的贡献会扣掉preipre_iprei那个位置的贡献)
    而对于[prei+1,i−1][pre_i+1,i-1][prei+1,i1]的点它们的SumSumSum全部会加上SumSumSum
    于是用线段树更新[prei+1,i−1][pre_i+1,i-1][prei+1,i1]的和以及历史和最值即可。
    代码:
#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
inline int read(){
	int ans=0;
	bool f=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f^=1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return f?ans:-ans;
}
const int N=1e5+5;
int n,a[N],pre[N],m;
ll ans[N];
struct qry{int l,id;};
vector<qry>q[N];
map<int,int>mp;
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (T[p].l+T[p].r>>1)
	struct Tag{ll add,hadd;};
	struct Val{ll sum,hsum;};
	const Tag tag_empty=(Tag){0ll,0ll};
	struct Node{int l,r;ll mx;Val val;Tag tag;}T[N<<2];
	inline Val operator+(const Val&a,const Val&b){return (Val){max(a.sum,b.sum),max(a.hsum,b.hsum)};}
	inline Val operator+(const Val&a,const Tag&b){return (Val){a.sum+b.add,max(a.hsum,a.sum+b.hadd)};}
	inline void operator+=(Val&a,const Tag&b){a=a+b;}
	inline Tag operator+(const Tag&a,const Tag&b){return (Tag){a.add+b.add,max(a.hadd,a.add+b.hadd)};}
	inline void operator+=(Tag&a,const Tag&b){a=a+b;}
	inline bool operator==(const Tag&a,const Tag&b){return a.add==b.add&&a.hadd==b.hadd;}
	inline void pushup(int p){T[p].val=T[lc].val+T[rc].val;}
	inline void pushnow(int p,Tag v){T[p].val+=v,T[p].tag+=v;}
	inline void pushdown(int p){
		if(T[p].tag==tag_empty)return;
		pushnow(lc,T[p].tag),pushnow(rc,T[p].tag);
		T[p].tag=tag_empty;
	}
	inline void build(int p,int l,int r){
		T[p].l=l,T[p].r=r,T[p].tag=tag_empty;
		if(T[p].l==T[p].r){T[p].val=(Val){a[l],a[l]};return;}
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
	inline void update(int p,int ql,int qr,int v){
		if(ql>qr)return;
		if(ql<=T[p].l&&T[p].r<=qr)return pushnow(p,(Tag){v,v});
		pushdown(p);
		if(qr<=mid)update(lc,ql,qr,v);
		else if(ql>mid)update(rc,ql,qr,v);
		else update(lc,ql,qr,v),update(rc,ql,qr,v);
		pushup(p);
	}
	inline ll query(int p,int ql,int qr){
		if(ql<=T[p].l&&T[p].r<=qr)return T[p].val.hsum;
		pushdown(p);
		if(qr<=mid)return query(lc,ql,qr);
		if(ql>mid)return query(rc,ql,qr);
		return max(query(lc,ql,qr),query(rc,ql,qr));
	}
	#undef lc
	#undef rc
	#undef mid
}
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read(),pre[i]=mp[a[i]+100001],mp[a[i]+100001]=i;
	SGT::build(1,1,n);
	m=read();
	for(ri l,r,i=1;i<=m;++i)l=read(),r=read(),q[r].push_back((qry){l,i});
	for(ri i=1;i<=n;++i){
		SGT::update(1,pre[i]+1,i-1,a[i]);
		for(ri j=0;j<q[i].size();++j)ans[q[i][j].id]=SGT::query(1,q[i][j].l,i);
	}
	for(ri i=1;i<=m;++i)cout<<max(ans[i],0ll)<<'\n';
	return 0;
}

GSS3

传送门
题意简述:单点修改维护最大子段和。


思路:直接上线段树即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=5e4+5;
int n,a[N];
inline int max(const int&a,const int&b){return a>b?a:b;}
inline int max(const int&a,const int&b,const int&c){return a>b?(a>c?a:c):(b>c?b:c);}
namespace SGT{
    #define lc (p<<1)
    #define rc (p<<1|1)
    #define mid (T[p].l+T[p].r>>1)
    struct Val{
        int ls,rs,ms,sum;
        inline void init(const int&x){ls=rs=ms=sum=x;}
    };
    inline Val operator+(const Val&a,const Val&b){return (Val){max(a.ls,a.sum+b.ls),max(b.rs,b.sum+a.rs),max(a.ms,b.ms,a.rs+b.ls),a.sum+b.sum};}
    struct Node{int l,r;Val val;}T[N<<2];
    inline void pushup(int p){T[p].val=T[lc].val+T[rc].val;}
    inline void build(int p,int l,int r){
        T[p].l=l,T[p].r=r;
        if(l==r)return T[p].val.init(a[l]);
        build(lc,l,mid),build(rc,mid+1,r),pushup(p);
    }
    inline void update(int p,int k,int v){
    	if(T[p].l==T[p].r)return T[p].val.init(v);
    	update(k<=mid?lc:rc,k,v),pushup(p);
	}
    inline Val query(int p,int ql,int qr){
        if(ql<=T[p].l&&T[p].r<=qr)return T[p].val;
        if(qr<=mid)return query(lc,ql,qr);
        if(ql>mid)return query(rc,ql,qr);
        return query(lc,ql,qr)+query(rc,ql,qr);
    }
}
inline int read(){
    int ans=0;
    bool f=1;
    char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f^=1;ch=getchar();}
    while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
    return f?ans:-ans;
}
int main(){
    n=read();
    for(ri i=1;i<=n;++i)a[i]=read();
    SGT::build(1,1,n);
    for(ri tt=read(),op,x,y;tt;--tt){
    	op=read(),x=read(),y=read();
    	if(!op)SGT::update(1,x,y);
    	else cout<<SGT::query(1,x,y).ms<<'\n';
	}
    return 0;
}

GSS4

传送门
题意简述:支持区间开方,区间求和。


思路:线段树套路题,区间最大值大于111递归修改否则直接返回。
每个数被开方log(logn)log(log_n)log(logn)次之后就没了。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
typedef long long ll;
const int N=1e5+5;
int n;
ll a[N];
inline ll read(){
	ll ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (T[p].l+T[p].r>>1)
	struct Node{int l,r;ll mx,sum;}T[N<<2];
	inline void pushup(int p){
		T[p].mx=max(T[lc].mx,T[rc].mx);
		T[p].sum=T[lc].sum+T[rc].sum;
	}
	inline void build(int p,int l,int r){
		T[p].l=l,T[p].r=r;
		if(l==r){T[p].sum=T[p].mx=a[l];return;}
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
	inline void update(int p,int ql,int qr){
		if(T[p].mx<2)return;
		if(T[p].l==T[p].r){T[p].sum=T[p].mx=sqrt(T[p].mx);return;}
		if(qr<=mid)update(lc,ql,qr);
		else if(ql>mid)update(rc,ql,qr);
		else update(lc,ql,qr),update(rc,ql,qr);
		pushup(p);
	}
	inline ll query(int p,int ql,int qr){
		if(ql<=T[p].l&&T[p].r<=qr)return T[p].sum;
		if(qr<=mid)return query(lc,ql,qr);
		if(ql>mid)return query(rc,ql,qr);
		return query(lc,ql,qr)+query(rc,ql,qr);
	}
	#undef lc
	#undef rc 
	#undef mid
}
int main(){
	int cas=0;
	while(~scanf("%d",&n)){
		++cas;
		printf("Case #%d:\n",cas);
		for(ri i=1;i<=n;++i)a[i]=read();
		SGT::build(1,1,n);
		for(ri tt=read(),op,l,r;tt;--tt){
			op=read(),l=read(),r=read();
			if(l>r)swap(l,r);
			if(!op)SGT::update(1,l,r);
			else cout<<SGT::query(1,l,r)<<'\n';
		}
		puts("");
	}
	return 0;
}

GSS5

传送门
题意简述:
求左端点在[l1,r1][l1,r1][l1,r1],右端点在[l2,r2][l2,r2][l2,r2]之间的最大子段和。


思路:直接分[l1,r1],[l2,r2][l1,r1],[l2,r2][l1,r1],[l2,r2]是否相交讨论一下,之后就是最大子段和问题了。
不相交⇒[l1,r1]\Rightarrow[l1,r1][l1,r1]中最大后缀和+sumr1+1,l2−1+[l2,r2]+sum_{r1+1,l2-1}+[l2,r2]+sumr1+1,l21+[l2,r2]中最大前缀和。
相交⇒\Rightarrow讨论l1l1l1l2l2l2关系,r1r1r1r2r2r2关系更新答案。
代码:

#include<bits/stdc++.h>
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (T[p].l+T[p].r>>1)
#define N 10005
using namespace std;
inline int read(){
	int ans=0,w=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans*w;
}
int n,m,a[N],T_T;
struct Node{int l,r,ls,rs,ms,sum;}T[N<<2];
inline Node operator+(const Node&a,const Node&b){
	Node ret;
	ret.l=a.l,ret.r=b.r,ret.sum=a.sum+b.sum;
	ret.ls=max(a.ls,a.sum+b.ls);
	ret.rs=max(b.rs,b.sum+a.rs);
	ret.ms=max(max(a.ms,b.ms),a.rs+b.ls);
	return ret;
}
inline void build(int p,int l,int r){
	T[p].l=l,T[p].r=r;
	if(l==r){T[p].ls=T[p].rs=T[p].ms=T[p].sum=a[l];return;}
	build(lc,l,mid),build(rc,mid+1,r),T[p]=T[lc]+T[rc];
}
inline Node query(int p,int ql,int qr){
	if(ql>T[p].r||qr<T[p].l)return (Node){T[p].l,T[p].r,0,0,0,0};
	if(ql<=T[p].l&&T[p].r<=qr)return T[p];
	if(qr<=mid)return query(lc,ql,qr);
	if(ql>mid)return query(rc,ql,qr);
	return query(lc,ql,mid)+query(rc,mid+1,qr);
}
int main(){
	T_T=read();
	while(T_T--){
		n=read();
		for(int i=1;i<=n;++i)a[i]=read();
		build(1,1,n),m=read();
		while(m--){
			int l1=read(),r1=read(),l2=read(),r2=read();
			if(r1<l2){
				Node ansl=query(1,l1,r1),ansm=query(1,r1+1,l2-1),ansr=query(1,l2,r2);
				printf("%d\n",ansl.rs+ansm.sum+ansr.ls);
			}
			else{
				int ans=query(1,l2,r1).ms;
				if(l1<l2)ans=max(ans,query(1,l1,l2).rs+query(1,l2,r2).ls-a[l2]);
				if(r2>r1)ans=max(ans,query(1,l1,r1).rs+query(1,r1,r2).ls-a[r1]);
				printf("%d\n",ans);
			}
		}
	}
	
	return 0;
}

GSS6

传送门
题意简述:支持在序列中插入,删除,修改某个数的值,询问最大子段和。


思路:把最大子段和的求法搬上fhq_treapfhq\_treapfhq_treap即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
	int ans=0;
	bool f=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return f?ans:-ans;
}
typedef long long ll;
typedef pair<int,int> pii;
const int N=2e5+5;
int n;
char s[2];
inline int max(const ll&a,const ll&b){return a>b?a:b;}
inline int max(const ll&a,const ll&b,const ll&c){return a>b?(a>c?a:c):(b>c?b:c);}
namespace Bst{
	#define lc (son[p][0])
	#define rc (son[p][1])
	struct Val{
		ll ls,rs,ms,sum;
		inline void init(const ll&x){ls=rs=ms=sum=x;}
	};
	inline Val operator+(const Val&a,const Val&b){return (Val){max(a.ls,a.sum+b.ls),max(b.rs,b.sum+a.rs),max(a.ms,b.ms,a.rs+b.ls),a.sum+b.sum};}
	int rt=0,tot=0,son[N][2],siz[N],rd[N],val[N];
	Val f[N];
	inline int build(int v){return val[++tot]=v,rd[tot]=rand(),siz[tot]=1,son[tot][0]=son[tot][1]=0,f[tot].init(v),tot;}
	inline void pushup(int p){
		siz[p]=siz[lc]+1+siz[rc];
		f[p].init(val[p]);
		if(lc)f[p]=f[lc]+f[p];
		if(rc)f[p]=f[p]+f[rc];
	}
	inline int merge(int a,int b){
		if(!a||!b)return a+b;
		if(rd[a]>rd[b])return son[a][1]=merge(son[a][1],b),pushup(a),a;
		return son[b][0]=merge(a,son[b][0]),pushup(b),b;
	}
	inline pii split(int p,int k){
		if(!p)return pii(0,0);
		pii tmp;
		if(siz[lc]>=k)return tmp=split(lc,k),lc=tmp.se,pushup(p),pii(tmp.fi,p);
		return tmp=split(rc,k-siz[lc]-1),rc=tmp.fi,pushup(p),pii(p,tmp.se);
	}
	inline void insert(int id,int v){
		pii x=split(rt,id-1);
		rt=merge(merge(x.fi,build(v)),x.se);
	}
	inline void delet(int id){
		pii x=split(rt,id-1),y=split(x.se,1);
		rt=merge(x.fi,y.se);
	}
	inline void update(int id,int v){
		pii x=split(rt,id-1),y=split(x.se,1);
		val[y.fi]=v,f[y.fi].init(v);
		rt=merge(merge(x.fi,y.fi),y.se);
	}
	inline void query(int l,int r){
		pii x=split(rt,l-1),y=split(x.se,r-l+1);
		cout<<f[y.fi].ms<<'\n';
		rt=merge(x.fi,merge(y.fi,y.se));
	}
	#undef lc
	#undef rc
}
int main(){
	srand(time(NULL));
	n=read();
	for(ri i=1;i<=n;++i)Bst::insert(i,read());
	for(ri p,x,tt=read();tt;--tt){
		scanf("%s",s);
		if(s[0]=='I')p=read(),x=read(),Bst::insert(p,x);
		if(s[0]=='D')p=read(),Bst::delet(p);
		if(s[0]=='R')p=read(),x=read(),Bst::update(p,x);
		if(s[0]=='Q')p=read(),x=read(),Bst::query(p,x);
	}
	return 0;
}

GSS7

传送门
题意简述:支持给一棵树路径赋值,求最大子路径。


思路:最大子段和搬上树链剖分即可,注意合并时候的顺序这点小细节就很简单了。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
const int N=1e5+5,inf=1e9;
int n,tot=0,a[N],num[N],dep[N],top[N],fa[N],hson[N],siz[N],pred[N];
vector<int>e[N];
void dfs1(int p){
	siz[p]=1;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fa[p])continue;
		fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
}
void dfs2(int p,int tp){
	top[p]=tp,pred[num[p]=++tot]=p;
	if(!hson[p])return;
	dfs2(hson[p],tp);
	for(ri i=0,v;i<e[p].size();++i)if((v=e[p][i])!=fa[p]&&v!=hson[p])dfs2(v,v);
}
inline int max(const int&a,const int&b){return a>b?a:b;}
inline int max(const int&a,const int&b,const int&c){return a>b?(a>c?a:c):(b>c?b:c);} 
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (T[p].l+T[p].r>>1)
	struct Val{int ls,rs,ms,sum;};
	inline Val operator+(const Val&a,const Val&b){return (Val){max(a.ls,a.sum+b.ls),max(b.rs,b.sum+a.rs),max(a.ms,b.ms,a.rs+b.ls),a.sum+b.sum};}
	struct Node{
		int l,r,cov;Val val;
		inline void init(const int&x){
			val.sum=(r-l+1)*x;
			if(x>0)val.ls=val.rs=val.ms=val.sum;
			else val.ls=val.rs=val.ms=x;
		}
	}T[N<<2];
	inline void pushup(int p){T[p].val=T[lc].val+T[rc].val;}
	inline void pushnow(int p,int v){T[p].init(v),T[p].cov=v;}
	inline void pushdown(int p){if(inf^T[p].cov)pushnow(lc,T[p].cov),pushnow(rc,T[p].cov),T[p].cov=inf;}
	inline void build(int p,int l,int r){
		T[p].l=l,T[p].r=r,T[p].cov=inf;
		if(l==r)return T[p].init(a[pred[l]]);
		build(lc,l,mid),build(rc,mid+1,r),pushup(p);
	}
	inline void update(int p,int ql,int qr,int v){
		if(ql<=T[p].l&&T[p].r<=qr)return pushnow(p,v);
		pushdown(p);
		if(qr<=mid)update(lc,ql,qr,v);
		else if(ql>mid)update(rc,ql,qr,v);
		else update(lc,ql,qr,v),update(rc,ql,qr,v);
		pushup(p); 
	}
	inline Val query(int p,int ql,int qr){
		if(ql<=T[p].l&&T[p].r<=qr)return T[p].val;
		pushdown(p);
		if(qr<=mid)return query(lc,ql,qr);
		if(ql>mid)return query(rc,ql,qr);
		return query(lc,ql,qr)+query(rc,ql,qr);
	}
	inline int query(int p,int k){
		if(T[p].l==T[p].r)return T[p].val.sum;
		pushdown(p);
		return query(k<=mid?lc:rc,k);
	}
	#undef lc
	#undef rc
	#undef mid
}
inline void update(int x,int y,int v){
	while(top[x]^top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		SGT::update(1,num[top[x]],num[x],v),x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	SGT::update(1,num[y],num[x],v);
}
inline int lca(int x,int y){
	while(top[x]^top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=fa[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}
inline int query(int x,int y){
	int t=lca(x,y);
	SGT::Val ta=(SGT::Val){0,0,0,0},tb=(SGT::Val){0,0,0,0};
	while(top[x]^top[t])ta=SGT::query(1,num[top[x]],num[x])+ta,x=fa[top[x]];
	ta=SGT::query(1,num[t],num[x])+ta;
	while(top[y]^top[t])tb=SGT::query(1,num[top[y]],num[y])+tb,y=fa[top[y]];
	tb=SGT::query(1,num[t],num[y])+tb;
	return max(ta.ms,tb.ms,ta.ls+tb.ls-SGT::query(1,num[t]));
}
inline int read(){
	int ans=0;
	bool f=1;
	char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f^=1;ch=getchar();}
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return f?ans:-ans;
}
int main(){
    n=read();
    for(ri i=1;i<=n;++i)a[i]=read();
    for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	dfs1(1),dfs2(1,1),SGT::build(1,1,n);
	for(ri tt=read(),op,l,r;tt;--tt){
		op=read(),l=read(),r=read();
		if(op==1)cout<<query(l,r)<<'\n';
		else update(l,r,read());
	} 
    return 0;
}

GSS8

传送门
题意简述:支持给一个序列插入,删除,修改某个数的值。
支持查询f(l,r,k)=∑i=lrai(i−l+1)k,0≤k≤10f(l,r,k)=\sum_{i=l}^ra_i(i-l+1)^k,0\le k\le10f(l,r,k)=i=lrai(il+1)k,0k10


思路:
显然除了查询外的都是fhqtreapfhq_treapfhqtreap常规操作。
然后只用考虑如何维护答案,由于每次的kkk一样,因此我们每个treaptreaptreap节点 对于k=0→10k=0\rightarrow10k=010都维护一个答案。
考虑合并a,ba,ba,b的答案。
aaa对于答案的贡献就是本身, 但是bbb不太一样。
本来假设是∑(x+t)k→∑(x+t+siza)k\sum(x+t)^k\rightarrow\sum(x+t+siz_a)^k(x+t)k(x+t+siza)k我们发现sizasiz_asiza是定值,于是可以将x+tx+tx+t看成yyy,我们已经知道y0,y1,...,y10y^0,y^1,...,y^10y0,y1,...,y10了,于是可以二项式展开(y+siza)k=∑i=0kCkiyisizak−i(y+siz_a)^k=\sum_{i=0}^kC_k^iy^isiz_a^{k-i}(y+siza)k=i=0kCkiyisizaki,我们每次预处理一个siza0→10siz_a^{0\rightarrow10}siza010转移即可。
代码:

#include<bits/stdc++.h>
#define ri register int
#define fi first
#define se second
using namespace std;
typedef unsigned int ll;
typedef pair<int,int> pii;
inline ll read(){
	ll ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=2e5+5;
int n;
ll pmul[11],C[11][11];
inline void init(){
	for(ri i=0;i<=10;++i){
		C[i][0]=C[i][i]=1;
		for(ri j=1;j<i;++j)C[i][j]=C[i-1][j]+C[i-1][j-1];
	}
}
namespace Bst{
	#define lc (son[p][0])
	#define rc (son[p][1])
	struct Val{
		ll s[11];int siz;
		inline void init(const ll&v){
			siz=1;
			for(ri i=0;i<=10;++i)s[i]=v;
		}
	}val[N];
	int son[N][2],a[N],rd[N],rt=0,tot=0;
	inline Val operator+(const Val&a,const Val&b){
		Val ret;
		ret.siz=a.siz+b.siz;
		pmul[0]=1;
		for(ri i=1;i<=10;++i)pmul[i]=pmul[i-1]*a.siz;
		for(ri i=0;i<=10;++i){ret.s[i]=a.s[i];for(ri j=0;j<=i;++j)ret.s[i]+=C[i][j]*b.s[j]*pmul[i-j];}
		return ret;
	}
	inline int build(ll v){return a[++tot]=v,rd[tot]=rand(),son[tot][0]=son[tot][1]=0,val[tot].init(v),tot;}
	inline void pushup(int p){
		val[p].init(a[p]);
		if(lc)val[p]=val[lc]+val[p];
		if(rc)val[p]=val[p]+val[rc];
	}
	inline int merge(int a,int b){
		if(!a||!b)return a+b;
		if(rd[a]>rd[b])return son[a][1]=merge(son[a][1],b),pushup(a),a;
		return son[b][0]=merge(a,son[b][0]),pushup(b),b;
	}
	inline pii split(int p,int k){
		if(!p)return pii(0,0);
		pii tmp;
		if(val[lc].siz>=k)return tmp=split(lc,k),lc=tmp.se,pushup(p),pii(tmp.fi,p);
		return tmp=split(rc,k-val[lc].siz-1),rc=tmp.fi,pushup(p),pii(p,tmp.se);
	}
	inline void insert(int id,ll v){
		pii x=split(rt,id-1);
		rt=merge(x.fi,merge(build(v),x.se));
	}
	inline void delet(int id){
		pii x=split(rt,id-1),y=split(x.se,1);
		rt=merge(x.fi,y.se);
	}
	inline void update(int id,ll v){
		pii x=split(rt,id-1),y=split(x.se,1);
		val[y.fi].init(a[y.fi]=v),rt=merge(x.fi,merge(y.fi,y.se));
	}
	inline void query(int l,int r,int k){
		pii x=split(rt,l-1),y=split(x.se,r-l+1);
		cout<<val[y.fi].s[k]<<'\n';
		rt=merge(x.fi,merge(y.fi,y.se));
	}
	#undef lc
	#undef rc
}
char s[5];
int main(){
	srand(time(NULL));
	n=read(),init();
	for(ri i=1;i<=n;++i)Bst::insert(i,read());
	for(ri l,r,x,tt=read();tt;--tt){
		scanf("%s",s);
		if(s[0]=='Q')l=read()+1,r=read()+1,Bst::query(l,r,read());
		if(s[0]=='I')x=read()+1,Bst::insert(x,read());
		if(s[0]=='D')x=read()+1,Bst::delet(x);
		if(s[0]=='R')x=read()+1,Bst::update(x,read());
	}
	return 0;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/10582406.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值