记录一下本人目前会做的 GSS 系列题目。
目前已经完成 GSS1,GSS3,GSS4,GSS5。
<h2>GSS1</h2>
<h3>题目描述</h3>
求最大子段和,序列的值可能为负数。
其中长度 $\leq 50000$。
<h3>解题报告</h3>
线段树维护这个序列。
线段树中维护四个值:$sum,lans,rans,ans$,分别表示当前序列的和,前缀最大值,后缀最大值,和最大子段和。
合并的时候暴力维护一下就可以了。
时间复杂度 $O(nlog_2n)$。
<h3>代码</h3>
#include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdio> #include <vector> #include <cstdlib> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #define Re register #define LL long long #define U unsigned #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c) #define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c) #define CLR(i,a) memset(i,a,sizeof(i)) #define BR printf("--------------------n") #define DEBUG(x) std::cerr << #x << '=' << x << std::endl namespace fastIO{ #define BUF_SIZE 100000 #define OUT_SIZE 100000 #define ll long long bool IOerror=0; inline char nc(){ static char buf[BUF_SIZE],p1=buf+BUF_SIZE,pend=buf+BUF_SIZE; if (p1==pend){ p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin); if (pend==p1){IOerror=1;return -1;} } return *p1++; } inline bool blank(char ch){return ch==' '||ch=='n'||ch=='r'||ch=='t';} inline void read(int &x){ bool sign=0; char ch=nc(); x=0; for (;blank(ch);ch=nc()); if (IOerror)return; if (ch=='-')sign=1,ch=nc(); for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0'; if (sign)x=-x; } inline void read(ll &x){ bool sign=0; char ch=nc(); x=0; for (;blank(ch);ch=nc()); if (IOerror)return; if (ch=='-')sign=1,ch=nc(); for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0'; if (sign)x=-x; } #undef ll #undef OUT_SIZE #undef BUF_SIZE }; using namespace fastIO; const int MAXN = 50000 + 5; struct Node New(int ,int ,Node ,Node *); struct Node{ int l,r;LL lmax,rmax,ans,sum; Node lc,rc; inline void init(){ l = r = sum = 0; lmax = rmax = ans = LLONG_MIN; lc = rc = NULL; } static Node *build(int l,int r){ int mid = (l + r) >> 1; return (l == r) ? New(l,r,NULL,NULL) : New(l,r,build(l,mid),build(mid+1,r)); } inline void pushup(){ lmax = std::max(lc->lmax,lc->sum+rc->lmax); rmax = std::max(rc->rmax,rc->sum+lc->rmax); sum = lc->sum + rc->sum; ans = std::max(std::max(lc->ans,rc->ans),lc->rmax+rc->lmax); } inline void update(int x,int pos){ if(l == r){ lmax = rmax = sum = ans = x; return; } int mid = (l + r) >> 1; if(pos <= mid) lc->update(x,pos); else rc->update(x,pos); pushup(); } inline Node query(int L,int R){ if(L == l && R == r){ return (Node){0,0,lmax,rmax,ans,sum,NULL,NULL}; } int mid = (l + r) >> 1; if(R <= mid) return lc->query(L,R); if(L > mid) return rc->query(L,R); Node la = lc->query(L,mid),ra = rc->query(mid+1,R),ret; ret.lmax = std::max(la.lmax,la.sum+ra.lmax); ret.rmax = std::max(ra.rmax,ra.sum+la.rmax); ret.sum = la.sum + ra.sum; ret.ans = std::max(std::max(la.ans,ra.ans),la.rmax+ra.lmax); return ret; } }pool[(MAXN<<1)+1],frog = pool,segt; Node New(int l,int r,Node lc,Node *rc){ Node *ret = ++frog; ret->l = l;ret->r = r; ret->lmax = ret->rmax = ret->ans = LLONG_MIN; ret->sum = 0; ret->lc = lc;ret->rc = rc; return ret; } int N,M; int main(){ read(N); segt = Node::build(1,N); FOR(i,1,N){ int x;read(x); segt->update(x,i); } read(M); while(M--){ int x,y;read(x);read(y); printf("%lldn",segt->query(x,y).ans); } return 0; }
<h2>GSS3</h2>
一点都没变......中间直接加上单点修改的操作就行了。
可以去我之前写的博客里观看,写的比较详细。
<h2>GSS4</h2>
<h3>题目描述</h3>
维护一个序列,支持下列操作:
- 区间开方(向下取整)
- 区间和
其中区间长度 $\leq 10^5$,所有元素的和 $\leq 10^{18}$。
<h3>解题报告</h3>
发现数一直开方最后会变成 $0$ 或 $1$ 。我们直接维护区间最大值就可以,当最大值 $\leq 1$ 的时候就不需要修改,否则暴力递归下去即可。因为数不是很大,$10^9$ 范围的数开方 $9$ 次就不用开方了,复杂度得以保证。
<h3>代码</h3>
#include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdio> #include <vector> #include <cstdlib> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #define Re register #define LL long long #define U unsigned #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c) #define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c) #define CLR(i,a) memset(i,a,sizeof(i)) #define BR printf("--------------------n") #define DEBUG(x) std::cerr << #x << '=' << x << std::endl const int MAXN = 200000 + 5; struct Node New(int ,int ,Node ,Node *); struct Node{ int l,r; LL sum,max; Node lc,rc; static Node *build(int l,int r){ int mid = (l + r) >> 1; return (l == r) ? New(l,r,NULL,NULL) : New(l,r,build(l,mid),build(mid+1,r)); } inline void pushup(){ sum = lc->sum + rc->sum; max = std::max(lc->max,rc->max); } inline void update(LL x,int pos){ if(l == r){ max = sum = x; return; } int mid = (l + r) >> 1; if(pos <= mid) lc->update(x,pos); else rc->update(x,pos); pushup(); } inline void modify(int L,int R){ if(max <= 1) return; if(l == r){ max = sum = std::sqrt(sum); return; } int mid = (l + r) >> 1; if(R <= mid) lc->modify(L,R); else if(L > mid) rc->modify(L,R); else lc->modify(L,mid),rc->modify(mid+1,R); pushup(); } inline LL query(int L,int R){ if(l == L && r == R) return sum; int mid = (l + r) >> 1; if(R <= mid) return lc->query(L,R); if(L > mid) return rc->query(L,R); return lc->query(L,mid)+rc->query(mid+1,R); } }pool[MAXN<<2],frog = pool,segt; Node New(int l,int r,Node lc,Node *rc){ Node *ret = ++frog; ret->l = l;ret->r = r; ret->lc = lc;ret->rc = rc; ret->sum = 0ll;ret->max = LLONG_MIN; return ret; } int N,M,ts; inline void Solve(){ printf("Case #%d:n",++ts); frog = pool;segt = NULL; CLR(pool,0); segt = Node::build(1,N); FOR(i,1,N){ LL x;scanf("%lld",&x); segt->update(x,i); } scanf("%d",&M); while(M--){ int opt,x,y; scanf("%d%d%d",&opt,&x,&y); if(x > y) std::swap(x,y); if(opt){ printf("%lldn",segt->query(x,y)); } else{ segt->modify(x,y); } } puts(""); } int main(){ while(~scanf("%d",&N)) Solve(); return 0; }
<h2>GSS5</h2>
<h3>题目描述</h3>
$GSS1$的升级版。这次限定左端点在 $[x_1,y_1]$ 之间,右端点在 $[x_2,y_2]$ 之间。
<h3>解题报告</h3>
只需要分类讨论一下就可以了qwq
如果 $y_1 \leq x_2$ 即两段区间没有交点,那么一定是 $[x_1,y_1]$ 的后缀最大值加上 $[y_1,x_2]$ 的和(必须经过)加上 $[x_2,y_2]$ 的前缀最大值。
如果有交点 ($y_1 > x_2$),那么就要进行一波分类讨论:
第一条线段:当 $x_2 \leq x,y \leq y_1$ 的时候,只需要求出 $[x_2,y_1]$ 的最大子段和。
第二条线段:当 $x_1 \leq x < x_2,x_2 \leq y \leq y_1$ 的时候,答案是 $[x_1,x_2)$ 的最大后缀和 $+$ $[x_2,y_1] $ 的最大前缀和。
第三条线段:当 $x_2 \leq x \leq y_1,y_1 < y \leq y_2$ 的时候,答案是$[x_2,y_1]$ 的最大后缀和 $+$ $(y_1,y_2]$ 的最大前缀和。
第四条线段:当 $x_1 \leq x < x_2,y_1 < y \leq y_2$ 的时候,答案是 $[x_1,x_2)$ 的最大后缀和 $+$ $[x_2,y_1]$ 的区间和 $+$ $(y_1,y_2]$ 的最大前缀和。
注意特判一下不是必选的区间和 $0$ 比较就可以了,还有一定要在查询的时候判断区间不可行退出,还要注意区间临界点处理的细节。
还有不要定义 $y1,y2$ 这种变量,会和 $cmath$ 库冲突的。
总体来说还是很水的,代码细节比较多
<h3>代码</h3>
#include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdio> #include <vector> #include <cstdlib> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #define Re register #define LL long long #define U unsigned #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c) #define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c) #define CLR(i,a) memset(i,a,sizeof(i)) #define BR printf("--------------------n") #define DEBUG(x) std::cerr << #x << '=' << x << std::endl namespace fastIO{ #define BUF_SIZE 100000 #define OUT_SIZE 100000 #define ll long long bool IOerror=0; inline char nc(){ static char buf[BUF_SIZE],p1=buf+BUF_SIZE,pend=buf+BUF_SIZE; if (p1==pend){ p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin); if (pend==p1){IOerror=1;return -1;} } return *p1++; } inline bool blank(char ch){return ch==' '||ch=='n'||ch=='r'||ch=='t';} inline void read(int &x){ bool sign=0; char ch=nc(); x=0; for (;blank(ch);ch=nc()); if (IOerror)return; if (ch=='-')sign=1,ch=nc(); for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0'; if (sign)x=-x; } inline void read(ll &x){ bool sign=0; char ch=nc(); x=0; for (;blank(ch);ch=nc()); if (IOerror)return; if (ch=='-')sign=1,ch=nc(); for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0'; if (sign)x=-x; } #undef ll #undef OUT_SIZE #undef BUF_SIZE }; //using namespace fastIO; #define read(x) std::cin >> x const int MAXN = 20000 + 5; #define int long long struct Node New(int ,int ,Node ,Node *); struct Node{ int l,r;LL lmax,rmax,ans,sum; Node lc,rc; inline void init(){ l = r = sum = 0; lmax = rmax = ans = LLONG_MIN; lc = rc = NULL; } static Node *build(int l,int r){ int mid = (l + r) >> 1; return (l == r) ? New(l,r,NULL,NULL) : New(l,r,build(l,mid),build(mid+1,r)); } inline void pushup(){ lmax = std::max(lc->lmax,lc->sum+rc->lmax); rmax = std::max(rc->rmax,rc->sum+lc->rmax); sum = lc->sum + rc->sum; ans = std::max(std::max(lc->ans,rc->ans),lc->rmax+rc->lmax); } inline void update(int x,int pos){ if(l == r){ lmax = rmax = sum = ans = x; return; } int mid = (l + r) >> 1; if(pos <= mid) lc->update(x,pos); else rc->update(x,pos); pushup(); } inline Node query(int L,int R){ if(L>R) return (Node){0,0,0,0,0,0,NULL,NULL}; if(L == l && R == r){ return (Node){0,0,lmax,rmax,ans,sum,NULL,NULL}; } int mid = (l + r) >> 1; if(R <= mid) return lc->query(L,R); if(L > mid) return rc->query(L,R); Node la = lc->query(L,mid),ra = rc->query(mid+1,R),ret; ret.lmax = std::max(la.lmax,la.sum+ra.lmax); ret.rmax = std::max(ra.rmax,ra.sum+la.rmax); ret.sum = la.sum + ra.sum; ret.ans = std::max(std::max(la.ans,ra.ans),la.rmax+ra.lmax); return ret; } }pool[(MAXN<<2)+1],frog = pool,segt; Node New(int l,int r,Node lc,Node *rc){ Node *ret = ++frog; ret->l = l;ret->r = r; ret->lmax = ret->rmax = ret->ans = LLONG_MIN; ret->sum = 0; ret->lc = lc;ret->rc = rc; return ret; } int N,M; inline void Solve(){ read(N); frog = pool;CLR(pool,0); segt = Node::build(1,N); FOR(i,1,N){ int x;read(x); segt->update(x,i); } int x1,y_1,x2,y_2; read(M);LL ans; while(M--){ read(x1);read(y_1);read(x2);read(y_2); if(y_1 < x2) ans = std::max(segt->query(x1,y_1-1).rmax,0ll) + segt->query(y_1,x2).sum + std::max(segt->query(x2+1,y_2).lmax,0ll); else{ ans = segt->query(x2,y_1).ans; ans = std::max(ans,segt->query(x2,y_1).lmax + segt->query(x1,x2-1).rmax); ans = std::max(ans,segt->query(y_1+1,y_2).lmax + segt->query(x2,y_1).rmax); ans = std::max(ans,segt->query(x2,y_1).sum + std::max(segt->query(x1,x2-1).rmax,0ll) + std::max(segt->query(y_1+1,y_2).lmax,0ll)); } printf("%lldn",ans); } } signed main(){ int T;read(T); while(T--) Solve(); return 0; }