[SPOJ]GSS系列(区间查询最大子段和问题)

1.GSS系列问题

s p o j spoj 上的一组数据结构问题
通常是查询最大子段和
找到这组题还是要感谢 a k i o i akioi g j m gjm
G S S = G r e a t e s t   S u m s   o f   S u b s e g m e n t s GSS=Greatest\ Sums \ of \ Subsegments
这里是按我洛谷认为的难度顺序排序的
c o n t i n u e   u p d a t i n g . . . . . . continue\ updating......

2.GSS1

GSS系列第一题传送门
纯粹的维护最大子段和
怎么做呢?
我们考虑,在合并两颗子树的时候,最大子段和会在哪一部分
1.在左子树内=> u . a n s = l s o n . a n s u.ans=lson.ans
2.在右子树内=> u . a n s = r s o n . a n s u.ans=rson.ans
3.在中间=> u . a n s = l s o n . l m a x + r s o n . l m a x u.ans=lson.lmax+rson.lmax
其中 l m a x lmax 表示最大前缀和, r m a x rmax 表示最大后缀和
那么我们考虑最大前缀和和最大后缀和怎么确定
以最大前缀和为例
1.左子树的最大前缀和=> u . l m a x = l s o n . l m a x u.lmax=lson.lmax
2.左子树+右子树的最大前缀和=> u . l m a x = l s o n . s u m + r s o n . l m a x u.lmax=lson.sum+rson.lmax
其中 s u m sum 表示区间和

然后就没了

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <stack>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <ctime>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;
const int N=1e5+5;
const int inf=0x7fffffff;
const double eps=1e-7;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

int n,m;
int a[N];

struct segment_tree{
	int sumf,sumb;
	int sum;
	int ans; 
	int l,r;
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1)

void pushup(int u){
	seg[u].sumf=max(seg[lc].sumf,seg[lc].sum+seg[rc].sumf);
	seg[u].sumb=max(seg[rc].sumb,seg[rc].sum+seg[lc].sumb);
	seg[u].sum=seg[lc].sum+seg[rc].sum;
	seg[u].ans=max(max(seg[lc].ans,seg[rc].ans),seg[lc].sumb+seg[rc].sumf);
} 

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r){seg[u].sumf=seg[u].sumb=seg[u].sum=seg[u].ans=a[l];return;}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(u);	
}

segment_tree query(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u];
	int mid=seg[u].l+seg[u].r>>1;
	if(r<=mid)return query(lc,l,r);
	if(l>mid)return query(rc,l,r);
	segment_tree ls=query(lc,l,r),rs=query(rc,l,r),res;
	res.sumf=max(ls.sumf,ls.sum+rs.sumf);
	res.sumb=max(rs.sumb,rs.sum+ls.sumb);
	res.sum=ls.sum+rs.sum;
	res.ans=max(max(ls.ans,rs.ans),ls.sumb+rs.sumf);
	return res;
} 

int main()
{
	read(n);
	Rep(i,1,n)read(a[i]);
	build(1,1,n);
	read(m);
	Rep(i,1,m){
		int l,r;
		read(l),read(r);
		printf("%d\n",query(1,l,r).ans);	
	}
	return 0;
}

3.GSS3

传送门
这道题跟上一个没啥区别…
加一个 u p d a t e update 就好了

对了,这题可以算三倍经验
算上GSS1,还有一道小白逛公园

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <stack>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <ctime>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;
const int N=1e5+5;
const int inf=0x7fffffff;
const double eps=1e-7;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

int n,m;
int a[N];

struct segment_tree{
	int sumf,sumb;
	int sum;
	int ans; 
	int l,r;
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1)

void pushup(int u){
	seg[u].sumf=max(seg[lc].sumf,seg[lc].sum+seg[rc].sumf);
	seg[u].sumb=max(seg[rc].sumb,seg[rc].sum+seg[lc].sumb);
	seg[u].sum=seg[lc].sum+seg[rc].sum;
	seg[u].ans=max(max(seg[lc].ans,seg[rc].ans),seg[lc].sumb+seg[rc].sumf);
} 

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r){seg[u].sumf=seg[u].sumb=seg[u].sum=seg[u].ans=a[l];return;}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(u);	
}

void update(int u,int x,int k){
	if(seg[u].l==seg[u].r){seg[u].sumf=seg[u].sumb=seg[u].sum=seg[u].ans=k;return;}
	int mid=seg[u].l+seg[u].r>>1;
	if(x<=mid)update(lc,x,k);
	else update(rc,x,k);
	pushup(u);	
}

segment_tree query(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u];
	int mid=seg[u].l+seg[u].r>>1;
	if(r<=mid)return query(lc,l,r);
	if(l>mid)return query(rc,l,r);
	segment_tree ls=query(lc,l,r),rs=query(rc,l,r),res;
	res.sumf=max(ls.sumf,ls.sum+rs.sumf);
	res.sumb=max(rs.sumb,rs.sum+ls.sumb);
	res.sum=ls.sum+rs.sum;
	res.ans=max(max(ls.ans,rs.ans),ls.sumb+rs.sumf);
	return res;
} 

int main()
{
	read(n);
	Rep(i,1,n)read(a[i]);
	build(1,1,n);
	read(m);
	Rep(i,1,m){
		int opt,x,y;
		read(opt),read(x),read(y);
		if(!opt)update(1,x,y);
		if(opt)printf("%d\n",query(1,x,y).ans);	
	}
	return 0;
}

4.GSS4

传送门
这道题好像跟最大子段和没什么关系。。。

我们观察发现 a i a_i 1 0 18 10^{18} 次方级别,但是他开最多六次根就变成1了,这样继续下去是不会变的,所以当一个区间全是1的时候,就不用管他了,下次再询问的时候直接返回区间长度‘’
如果不是1就暴力修改就好了

这里用的是树状数组和并查集

对了,这题也是双倍经验
上帝造题的七分钟2 花神游历各国

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <stack>
# include <ctime>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define mct(a,b) memset(a,b,sizeof(a))
# define gc getchar()
typedef long long ll;
const int N=1e6+5;
const int inf=0x7fffffff;
const int mod=1e9+7;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

# define int ll

int n,m;
ll a[N];
ll bit[N];
int fa[N];

inline int lowbit(int o){
	return o&(-o);	
}

inline void add(int o,int x){
	for(;o<=n;o+=lowbit(o))bit[o]+=x;	
}

inline ll ask(int o){
	ll res=0;
	for(;o;o-=lowbit(o))res+=bit[o];
	return res;	
}

int find(int x){
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);	
}

inline void update(int l,int r){
	for(int i=l;i<=r;i++,i=find(i)){
		ll s=1ll*sqrt(a[i]);
		add(i,(s-a[i]));
		a[i]=s;
		if(a[i]<=1)fa[i]=i+1;
	}
}

signed main()
{
	read(n);
	Rep(i,1,n)read(a[i]),add(i,a[i]);
	Rep(i,1,N-1)fa[i]=i;
	read(m);
	Rep(i,1,m){
		ll opt,x,y;
		read(opt),read(x),read(y);
		if(x>y)swap(x,y);
		if(!opt)update(x,y);
		else printf("%lld\n",ask(y)-ask(x-1));
	}
	return 0;
}

5.GSS2

传送门
人生第三道黑题呀QAQ
虽然这道题只比GSS1多几个字,难度真的是难了不只一点

对于这种奇奇怪怪的题目,我们可以考虑离线做(因为他并没有强制在线)
我们先将询问离线,然后按照右端点排序,为什么要这么做呢?我们就可以从1到 n n 扫一遍然后再做
我们先不考虑去重的问题
当我们扫到 i i 的时候
我们让线段树的第 j j 叶子节点维护 a [ j ] + a [ j + 1 ] + a [ j + 2 ] . . . + a [ i ] a[j]+a[j+1]+a[j+2]...+a[i] 的和
对于询问 [ l , r ] [l,r] 的时候的答案是第 l l ~ r r 的所有叶子节点的最大值

我们再来考虑去重,我们在从左往右扫的时候,我们只要把 [ l a s t [ a [ i ] ] + 1 , i ] [last[a[i]]+1,i] 这一区间里的点加上 a [ i ] a[i] 就可以了,其中 l a s t [ a [ i ] ] last[a[i]] 表示 a [ i ] a[i] 上一次在哪里出现过

然后就没有什么了吧…
线段树维护两个量,一个是这个子树的和,一个是这个子树包含的所有叶子节点的最大值
因为我们需要区间修改,所以再来两个懒标记

然后因为 a i a_i 可能有负数,所以要把 l a s t last 数组平移一下就好了QAQ

这题不知道有没有双倍经验,反正我目前没找到

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <stack>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <ctime>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;
const int N=1e5+5;
const int mod=1e9+7;
const int move=1e5;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

int n,m;
int a[N],last[N<<1],pre[N];
int out[N];

struct segment_tree{
	int l,r;
	int sum,ans;
	int tagsum,tagans;	
}seg[N<<2];

struct Query{
	int l,r,id;
	bool operator < (const Query &cmp)const{
		return r<cmp.r;
	}
}q[N];

# define lc (u<<1)
# define rc (u<<1|1)

void pushup(int u){
	seg[u].sum=max(seg[lc].sum,seg[rc].sum);
	seg[u].ans=max(seg[lc].ans,seg[rc].ans);	
}

void pushdown(int u){
	seg[lc].ans=max(seg[lc].ans,seg[lc].sum+seg[u].tagans);
	seg[lc].sum+=seg[u].tagsum;
	seg[lc].tagans=max(seg[lc].tagans,seg[lc].tagsum+seg[u].tagans);
	seg[lc].tagsum+=seg[u].tagsum;
	seg[rc].ans=max(seg[rc].ans,seg[rc].sum+seg[u].tagans);
	seg[rc].sum+=seg[u].tagsum;
	seg[rc].tagans=max(seg[rc].tagans,seg[rc].tagsum+seg[u].tagans);
	seg[rc].tagsum+=seg[u].tagsum;
	seg[u].tagsum=seg[u].tagans=0;
}

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r)return;
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);	
}

void update(int u,int l,int r,int k){
	if(seg[u].l>=l&&seg[u].r<=r){
		seg[u].sum+=k;
		seg[u].ans=max(seg[u].sum,seg[u].ans);
		seg[u].tagsum+=k;
		seg[u].tagans=max(seg[u].tagans,seg[u].tagsum);
		return;
	}	
	pushdown(u);
	int mid=seg[u].l+seg[u].r>>1;
	if(l<=mid)update(lc,l,r,k);
	if(r>mid)update(rc,l,r,k);
	pushup(u);
}

int query(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u].ans;
	pushdown(u);
	int mid=seg[u].l+seg[u].r>>1;
	int res=0;
	if(l<=mid)res=max(res,query(lc,l,r));
	if(r>mid)res=max(res,query(rc,l,r));
	return res;	
}

int main()
{
	read(n);
	Rep(i,1,n){
		read(a[i]);
		pre[i]=last[a[i]+move];//平移
		last[a[i]+move]=i;
	}	
	read(m);
	Rep(i,1,m){
		read(q[i].l),read(q[i].r);//离线
		q[i].id=i;
	}
	sort(q+1,q+m+1);
	build(1,1,n);
	int j=1;
	Rep(i,1,n){
		update(1,pre[i]+1,i,a[i]);//更新
		for(;j<=m&&q[j].r<=i;j++)
			out[q[j].id]=query(1,q[j].l,q[j].r);//查询
	}
	Rep(i,1,m)printf("%d\n",out[i]);
	return 0;
}

6.GSS5

传送门

人生第四道黑题呀QAQ

这道题我们很明显会有一个想法是线段是维护前缀和,每次从 [ x 1 , y 1 ] [x_1,y_1] 中找最小,在 [ x 2 , y 2 ] [x_2,y_2] 中找最大,然后一减
黑题?我切了—— c h i n a _ x y c china\_xyc
黑题可能让你那么简单就水过了吗…还有一句话呢

x 2 < y 1 x_2可能<y_1

这个东西怎么做呢?
我们考虑,当 x 2 < y 1 x_2<y_1 时,答案可能在哪里
1.左端点位于 [ x 1 , x 2 ] [x_1,x_2] ,右端点位于 [ x 2 , y 2 ] [x_2,y_2] =>上面最开始的做法
2.左端点位于 [ x 1 , y 1 ] [x_1,y_1] ,右端点位于 [ y 1 , y 2 ] [y_1,y_2] =>上面最开始的做法
3.左右端点都位于 [ x 2 , y 1 ] [x_2,y_1] 时间=>GSS3粘过来

所以这道题我们需要维护
1.前缀和的区间 m a x max
2.前缀和的区间 m i n min
3. a a 的最大子段和(以及剩下的三个东西)

然后就没了

对了,这道题我写前缀和一直 w a wa ,后来写了后缀和就A了???( 蜜汁

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <stack>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <ctime>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;
const int N=1e5+5;
const int mod=1e9+7;
const double eps=1e-7;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

int t,n,m;
int a[N];
int sum[N];

struct segment_tree{
	int l,r;
	int _min,_max;
	int sum,ans,lmax,rmax;	
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1)

void pushup(int u){
	seg[u]._min=min(seg[lc]._min,seg[rc]._min);
	seg[u]._max=max(seg[lc]._max,seg[rc]._max);
	seg[u].sum=seg[lc].sum+seg[rc].sum;
	seg[u].lmax=max(seg[lc].lmax,seg[lc].sum+seg[rc].lmax);
	seg[u].rmax=max(seg[rc].rmax,seg[rc].sum+seg[lc].rmax);
	seg[u].ans=max(seg[lc].rmax+seg[rc].lmax,max(seg[lc].ans,seg[rc].ans));
}

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r){
		seg[u]._min=sum[l];
		seg[u]._max=sum[l]+a[l];	//这里记得加上去,因为我们查询的时候多减去了一个a[l]
		seg[u].sum=seg[u].ans=seg[u].lmax=seg[u].rmax=a[l];
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(u);
}

int Getmax(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u]._max;
	int mid=seg[u].l+seg[u].r>>1;
	int res=-1e9;
	if(l<=mid)res=max(res,Getmax(lc,l,r));
	if(r>mid)res=max(res,Getmax(rc,l,r));
	return res;	
}

int Getmin(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u]._min;
	int mid=seg[u].l+seg[u].r>>1;
	int res=1e9;
	if(l<=mid)res=min(res,Getmin(lc,l,r));
	if(r>mid)res=min(res,Getmin(rc,l,r));
	return res;	
}

segment_tree Getans(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u];
	int mid=seg[u].l+seg[u].r>>1;
	if(r<=mid)return Getans(lc,l,r);
	if(l>mid)return Getans(rc,l,r);
	segment_tree a,b,c;
	a=Getans(lc,l,r),b=Getans(rc,l,r);
	c.sum=a.sum+b.sum;
	c.lmax=max(a.lmax,a.sum+b.lmax);
	c.rmax=max(b.rmax,b.sum+a.rmax);
	c.ans=max(a.rmax+b.lmax,max(a.ans,b.ans));
	return c;
}

int main()
{
	read(t);
	while(t--){
		read(n);
		Rep(i,1,n)read(a[i]);
		sum[0]=0;
		_Rep(i,n-1,1)sum[i]=sum[i+1]+a[i+1];//后缀和
		build(1,1,n);
		read(m);
		Rep(i,1,m){
			int x1,y1,x2,y2;
			read(x1),read(y1),read(x2),read(y2);
			int ans=-1e9;
			if(y1<=x2)ans=max(ans,-Getmin(1,x2,y2)+Getmax(1,x1,y1));
			else{
				ans=max(ans,Getans(1,x2,y1).ans);
				ans=max(ans,-Getmin(1,y1,y2)+Getmax(1,x1,y1));
				ans=max(ans,-Getmin(1,x2,y2)+Getmax(1,x1,x2));
			}
			printf("%d\n",ans);
		}
	}
	return 0;
}

7.GSS6

题目传送门
这道题看到有插入删除操作,自然想到平衡树
然后就是继续按照GSS1和GSS3的做法,维护那几个量
就可以了
因为我比较菜,只会 s p l a y splay ,所以这题就打了个170多行的 s p l a y   q w q splay\ qwq
加上若干行优化到了220行量级

如果 w a wa 了就好好查查 u p d a t e update 有没有写对吧
像我update错3处
如果 t l e tle 了就把数组开大点,因为还有插入操作,开 1 e 5 1e5 是不够的

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <stack>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <ctime>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
const double eps=1e-7;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

# define debug puts("QAQ")

int n,m;
int a[N];
int fa[N],son[N][2],siz[N],sum[N],key[N],lmax[N],rmax[N],ans[N];
int tot,rt;

int create(int x,int faz){
	tot++;
	fa[tot]=faz;
	son[tot][0]=son[tot][1]=0;
	sum[tot]=key[tot]=ans[tot]=x;
	lmax[tot]=rmax[tot]=max(x,0);
	siz[tot]=1;
	return tot;	
}

bool locate(int x){return son[fa[x]][1]==x;}
void clear(int x){fa[x]=son[x][0]=son[x][1]=siz[x]=sum[x]=key[x]=lmax[x]=rmax[x]=ans[x]=0;}

void update(int u){
	siz[u]=1,sum[u]=key[u];
	if(son[u][0])siz[u]+=siz[son[u][0]],sum[u]+=sum[son[u][0]];	
	if(son[u][1])siz[u]+=siz[son[u][1]],sum[u]+=sum[son[u][1]];
	if(!son[u][0]&&!son[u][1]){
		ans[u]=key[u];
		lmax[u]=rmax[u]=max(key[u],0);
		return;
	}
	if(!son[u][0]){
		lmax[u]=max(key[u]+lmax[son[u][1]],0);
		rmax[u]=max(max(rmax[son[u][1]],sum[son[u][1]]+key[u]),0);
		ans[u]=max(ans[son[u][1]],lmax[son[u][1]]+key[u]);
		return;
	}
	if(!son[u][1]){
		lmax[u]=max(max(lmax[son[u][0]],sum[son[u][0]]+key[u]),0);
		rmax[u]=max(key[u]+rmax[son[u][0]],0);
		ans[u]=max(ans[son[u][0]],rmax[son[u][0]]+key[u]);
		return;	
	}
	else{
		lmax[u]=max(lmax[son[u][0]],sum[son[u][0]]+key[u]+lmax[son[u][1]]);
		rmax[u]=max(rmax[son[u][1]],sum[son[u][1]]+key[u]+rmax[son[u][0]]);
		ans[u]=max(rmax[son[u][0]]+key[u]+lmax[son[u][1]],max(ans[son[u][0]],ans[son[u][1]]));
		return;	
	}
}

void rotate(int x){
	int faz=fa[x],grand=fa[faz],side=locate(x);
	son[faz][side]=son[x][side^1],fa[son[faz][side]]=faz;
	son[x][side^1]=faz,fa[faz]=x;
	fa[x]=grand;
	if(grand)son[grand][son[grand][1]==faz]=x;
	update(faz),update(x);	
}

void splay(int x,int tar){
	if(!tar)rt=x;
	while(fa[x]!=tar){
		if(fa[fa[x]]!=tar)rotate(locate(x)==locate(fa[x])?fa[x]:x);
		rotate(x);	
	}
}

void build(int u,int l,int r){
	int mid=l+r>>1;
	key[u]=a[mid];
	if(l<mid){
		son[u][0]=create(0,u);
		build(son[u][0],l,mid-1);
	}
	if(r>mid){
		son[u][1]=create(0,u);
		build(son[u][1],mid+1,r);	
	}
	update(u);
}

int kth(int k){
	int u=rt;
	while(1){
		if(son[u][0]&&k<=siz[son[u][0]])u=son[u][0];
		else{
			int sum=siz[son[u][0]]+1;
			if(k<=sum)return u;
			u=son[u][1],k-=sum;	
		}
	}
}

void insert(int x,int k){
	splay(kth(x),0);
	splay(kth(x+1),rt);
	x=son[rt][1];
	son[x][0]=create(k,x);
	update(son[x][0]);
	update(x),update(rt);	
}

void erase(int x){
	splay(kth(x),0);
	splay(kth(x+1),rt);
	x=son[rt][1];
	fa[son[x][1]]=rt;
	son[rt][1]=son[x][1];
	clear(x);
	update(rt);	
}

void reset(int x,int k){
	splay(kth(x+1),0);
	key[rt]=k;
	update(rt);	
}

int query(int l,int r){
	splay(kth(l),0);
	splay(kth(r+2),rt);
	return ans[son[son[rt][1]][0]];	
}

int main()
{
	read(n);
	Rep(i,1,n)read(a[i]);
	rt=create(0,0);
	build(rt,0,n+1);
	read(m);
	Rep(i,1,m){
		char opt[10];
		int x,y;
		scanf("%s%d",opt,&x);
		switch(opt[0]){
			case 'I':read(y),insert(x,y);break;
			case 'D':erase(x);break;
			case 'R':read(y),reset(x,y);break;
			case 'Q':read(y),printf("%d\n",query(x,y));break;
		}
	}
	return 0;
}

8.GSS7

题目传送门
这个就是一个树链剖分套上 G S S 3 GSS3
但是注意我们在 m e r g e merge 两段的答案的时候, m e r g e merge 函数是不满足交换律的(想当然在这里 w a wa 了好久)
然后最后要把 x x 往上跳的那一段的左右最大值交换(因为要整段取反)

# include <cstdio>
# include <algorithm>
# include <cstring>
# include <cmath>
# include <climits>
# include <iostream>
# include <string>
# include <queue>
# include <stack>
# include <vector>
# include <set>
# include <map>
# include <cstdlib>
# include <ctime>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)

typedef long long ll;
const int N=1e5+5;
const int mod=1e9+7;
const double eps=1e-7;
template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

int n,q;
int a[N],_a[N];
int head[N],cnt;
int faz[N],son[N],dep[N],siz[N],dfn[N],top[N],tot; 

struct Edge{
	int to,next;
}e[N<<1];

void add(int x,int y){
	e[++cnt]=(Edge){y,head[x]},head[x]=cnt;	
}

struct node{
	int l,r;
	int cov,tag;
	int sum,lmax,rmax,ans;
	node(){cov=tag=sum=lmax=rmax=ans=l=r=0;}	
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1) 

void cover(int u,int k){
	seg[u].sum=k*(seg[u].r-seg[u].l+1);
	seg[u].lmax=seg[u].rmax=seg[u].ans=max(seg[u].sum,0);
	seg[u].cov=1;
	seg[u].tag=k;
}

void pushup(int u){
	seg[u].sum=seg[lc].sum+seg[rc].sum;
	seg[u].lmax=max(seg[lc].lmax,seg[lc].sum+seg[rc].lmax);
	seg[u].rmax=max(seg[rc].rmax,seg[rc].sum+seg[lc].rmax);
	seg[u].ans=max(seg[lc].rmax+seg[rc].lmax,max(seg[lc].ans,seg[rc].ans));	
}

node merge(node l,node r){
	node res;
	res.sum=l.sum+r.sum;
	res.lmax=max(l.lmax,l.sum+r.lmax);
	res.rmax=max(r.rmax,r.sum+l.rmax);
	res.ans=max(l.rmax+r.lmax,max(l.ans,r.ans));
	res.cov=res.tag=0;
	return res;	
}

void pushdown(int u){
	cover(lc,seg[u].tag);
	cover(rc,seg[u].tag);
	seg[u].cov=seg[u].tag=0;	
}

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r){
		seg[u].sum=_a[l];
		seg[u].lmax=seg[u].rmax=seg[u].ans=max(_a[l],0);
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(u);
}

void update(int u,int l,int r,int k){
	if(seg[u].l>=l&&seg[u].r<=r){
		cover(u,k);
		return;
	}	
	if(seg[u].cov)pushdown(u);
	int mid=seg[u].l+seg[u].r>>1;
	if(l<=mid)update(lc,l,r,k);
	if(r>mid)update(rc,l,r,k);
	pushup(u);
}

node query(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return seg[u];
	if(seg[u].cov)pushdown(u);
	int mid=seg[u].l+seg[u].r>>1;
	if(r<=mid)return query(lc,l,r);
	if(l>mid)return query(rc,l,r);
	node lson=query(lc,l,r),rson=query(rc,l,r);
	return merge(lson,rson);	
}

int RouteQuery(int x,int y){
	node l,r;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]){
			r=merge(query(1,dfn[top[y]],dfn[y]),r);
			y=faz[top[y]];
		}
		else{
			l=merge(query(1,dfn[top[x]],dfn[x]),l);
			x=faz[top[x]];	
		}
	}
	if(dep[x]>dep[y])l=merge(query(1,dfn[y],dfn[x]),l);
	else r=merge(query(1,dfn[x],dfn[y]),r);
	swap(l.lmax,l.rmax);
	return merge(l,r).ans;
}

void RouteCover(int x,int y,int k){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		update(1,dfn[top[x]],dfn[x],k);
		x=faz[top[x]];
	}
	if(dep[x]>dep[y])swap(x,y);
	update(1,dfn[x],dfn[y],k);
}

void dfs1(int u,int fa){
	faz[u]=fa;
	dep[u]=dep[fa]+1;
	siz[u]=1;
	RepG(i,u){
		int v=e[i].to;
		if(v==fa)continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}

void dfs2(int u,int _top){
	top[u]=_top;
	dfn[u]=++tot;
	_a[tot]=a[u];
	if(!son[u])return;
	dfs2(son[u],_top);
	RepG(i,u){
		int v=e[i].to;
		if(v==faz[u]||v==son[u])continue;
		dfs2(v,v);
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	read(n);
	Rep(i,1,n)read(a[i]);
	Rep(i,1,n-1){
		int x,y;
		read(x),read(y);
		add(x,y),add(y,x);
	}
	dfs1(1,0);
	dfs2(1,1);
	build(1,1,n);
	read(q);
	while(q--){
		int opt,x,y,z;
		read(opt),read(x),read(y);
		if(opt==1)printf("%d\n",RouteQuery(x,y));
		else read(z),RouteCover(x,y,z);	
	}
	return 0;
}

9.GSS8

题目传送门
占坑,待填
可能永远不会填了据说要用二项式定理之类的毒瘤东西

10.写在最后

这篇 b l o g blog 并没有更新完
还是要感谢 g j m gjm 找到了这么好的一组题

发布了45 篇原创文章 · 获赞 52 · 访问量 9638

猜你喜欢

转载自blog.csdn.net/devout_/article/details/104297733