GSS - Can you answer these queries I ~ ? (持续更新...)
\(\text{SPOJ}\) 毒瘤的 数据结构系列, 值得一做
GSS I :
给定一数列\(A\), 支持查询区间最大子段和
\(A[i] \le 15007,\ N\le 5e4\)
线段树常规做法 :
//知识点:线段树
/*
By:Luckyblock
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define max std::max
#define ls (now << 1)
#define rs (now << 1 | 1)
const int MARX = 5e4 + 10;
//===========================================================
struct SegmentTree
{
int L, R;
int Pre, Suf; //[L,R]的最大 前缀和, 后缀和
int Sum, Marx;//[L,R]的和, 最大子段和
} Tree[MARX << 2];
int N, M, A[MARX];
//===========================================================
inline int read()
{
int f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Updata(int now) //信息上传
{
Tree[now].Sum = Tree[ls].Sum + Tree[rs].Sum; //更新元素和
Tree[now].Marx = max(Tree[ls].Marx, Tree[rs].Marx); //更新 最大子段和
Tree[now].Marx = max(Tree[now].Marx, Tree[ls].Suf + Tree[rs].Pre);
Tree[now].Pre = max(Tree[ls].Pre, Tree[ls].Sum + Tree[rs].Pre); //前缀
Tree[now].Suf = max(Tree[rs].Suf, Tree[ls].Suf + Tree[rs].Sum); //后缀
}
void Build(int now, int L, int R) //建树
{
Tree[now].L = L, Tree[now].R = R;
if(L == R) //叶节点 赋初值
{
Tree[now].Pre = Tree[now].Suf = Tree[now].Sum = Tree[now].Marx = A[L];
return ;
}
int mid = (L + R) >> 1;
Build(ls, L, mid), Build(rs, mid + 1, R);
Updata(now);
}
SegmentTree Merge(SegmentTree L, SegmentTree R) //将相邻区间L与R的信息进行合并
{
SegmentTree ret;
ret.Sum = L.Sum + R.Sum; //元素和
ret.Marx = max(L.Marx, max(R.Marx, L.Suf + R.Pre)); //最大子段和
ret.Pre = max(L.Pre, L.Sum + R.Pre); //前缀
ret.Suf = max(R.Suf, L.Suf + R.Sum); //后缀
return ret;
}
SegmentTree Query(int now, int L, int R) //查询 [L,R]的最大子段和
{
if(L <= Tree[now].L && Tree[now].R <= R) return Tree[now]; //被全部包含
int mid = (Tree[now].L + Tree[now].R) >> 1;
if(L > mid) return Query(rs, L, R); //查询区间 只位于右子树
if(R <= mid) return Query(ls, L, R);//查询区间 只位于左子树
return Merge(Query(ls, L, R), Query(rs, L, R)); //将左右区间合并
}
//===========================================================
int main()
{
N = read(); for(int i = 1; i <= N; i ++) A[i] = read();
Build(1, 1, N);
M = read();
for(int i = 1, L, R; i <= M; i ++)
L = read(), R = read(),
printf("%d\n", Query(1, L, R).Marx);
return 0;
}
GSS2 II :
给定一数列\(A\), 支持查询区间最大子段和, 重复的数只做一次贡献
\(A[i] \in (- 1e5, 1e5],\ N, Q\le 5e4\)
GSS3 III :
给定一数列\(A\), 支持单点修改 , 查询区间最大子段和
\(A[i] \in [- 1e4, 1e4],\ N, Q\le 5e4\)
带单点修改的 I, 直接上代码 :
//知识点:线段树
/*
By:Luckyblock
*/
#include <cstdio>
#include <cctype>
#include <algorithm>
#define max std::max
#define ls (now << 1)
#define rs (now << 1 | 1)
const int MARX = 5e4 + 10;
//===========================================================
struct SegmentTree
{
int L, R;
int Pre, Suf; //[L,R]的最大 前缀和, 后缀和
int Sum, Marx;//[L,R]的和, 最大子段和
} Tree[MARX << 2];
int N, M, A[MARX];
//===========================================================
inline int read()
{
int f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Updata(int now) //信息上传
{
Tree[now].Sum = Tree[ls].Sum + Tree[rs].Sum; //更新元素和
Tree[now].Marx = max(Tree[ls].Marx, Tree[rs].Marx); //更新 最大子段和
Tree[now].Marx = max(Tree[now].Marx, Tree[ls].Suf + Tree[rs].Pre);
Tree[now].Pre = max(Tree[ls].Pre, Tree[ls].Sum + Tree[rs].Pre); //前缀
Tree[now].Suf = max(Tree[rs].Suf, Tree[ls].Suf + Tree[rs].Sum); //后缀
}
void Build(int now, int L, int R) //建树
{
Tree[now].L = L, Tree[now].R = R;
if(L == R) //叶节点 赋初值
{
Tree[now].Pre = Tree[now].Suf = Tree[now].Sum = Tree[now].Marx = A[L];
return ;
}
int mid = (L + R) >> 1;
Build(ls, L, mid), Build(rs, mid + 1, R);
Updata(now);
}
SegmentTree Merge(SegmentTree L, SegmentTree R) //将相邻区间L与R的信息进行合并
{
SegmentTree ret;
ret.Sum = L.Sum + R.Sum; //元素和
ret.Marx = max(L.Marx, max(R.Marx, L.Suf + R.Pre)); //最大子段和
ret.Pre = max(L.Pre, L.Sum + R.Pre); //前缀
ret.Suf = max(R.Suf, L.Suf + R.Sum); //后缀
return ret;
}
SegmentTree Query(int now, int L, int R) //查询 [L,R]的最大子段和
{
if(L <= Tree[now].L && Tree[now].R <= R) return Tree[now]; //被全部包含
int mid = (Tree[now].L + Tree[now].R) >> 1;
if(L > mid) return Query(rs, L, R); //查询区间 只位于右子树
if(R <= mid) return Query(ls, L, R);//查询区间 只位于左子树
return Merge(Query(ls, L, R), Query(rs, L, R)); //将左右区间合并
}
void Change(int now, int Pos, int Val) //单点修改
{
if(Tree[now].L == Tree[now].R && Tree[now].L == Pos) //向下深入至 叶节点
{
Tree[now].Pre = Tree[now].Suf = Tree[now].Sum = Tree[now].Marx = Val;
return ;
}
int mid = (Tree[now].L + Tree[now].R) >> 1;
if(Pos <= mid) Change(ls, Pos, Val);
else Change(rs, Pos, Val);
Updata(now);
}
//===========================================================
int main()
{
N = read(); for(int i = 1; i <= N; i ++) A[i] = read();
Build(1, 1, N);
M = read();
for(int i = 1; i <= M; i ++)
{
int opt = read(), L = read(), R = read();
if(opt == 0) Change(1, L, R);
else printf("%d\n", Query(1, L, R).Marx);
}
return 0;
}
这玩意似乎可以动态 \(DP\ ?\) 学了之后再更 .
GSS IV :
给定一数列\(A\), 支持区间开平方操作, 查询区间和
\(\sum\limits_{i=1}^{N} A[i] \le 10^{18},\ N, M \le 1e5\)
分块经典操作 , 但是此题卡掉了分块 ,
将分块思想应用至线段树.
由于开方运算不满足 一系列运算律, 无法使用标记法.
怎么办? 考虑直接暴力.
众所周知, \(\Bigg\lfloor\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt {10 ^{18}}}}}}}\Bigg\rfloor = 1\) .
对于一个数, 最多 6 次修改后 必然等于 1.
而 \(\sqrt{1} = 1\) , 修改操作对其不会有影响 .
则可以记录各某区间的开方次数.
若开方次数 \(> 5\), 则无论其修改次数, 元素和都不会改变, 不进行修改操作.
否则 直接深入到叶节点 暴力修改, 并在暴力修改时 完成对次数的更新.
//知识点:线段树
/*
By:Luckyblock
*/
#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#include <algorithm>
#define ls (now << 1)
#define rs (now << 1 | 1)
#define ll long long
const int MARX = 1e5 + 10;
//===========================================================
struct SegmentTree
{
int L, R, Num;
ll Sum;
} Tree[MARX << 2];
int N, M, T;
ll Original[MARX];
//===========================================================
inline ll read()
{
ll f = 1, w = 0; char ch = getchar();
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
for(; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void PushUp(int now) {Tree[now].Sum = Tree[ls].Sum + Tree[rs].Sum;} //更新区间和
void Build(int now, int L, int R) //建树
{
Tree[now].L = L, Tree[now].R = R;
if(L == R) {Tree[now].Sum = (ll) Original[L]; return ;}
int mid = (L + R) >> 1;
Build(ls, L, mid), Build(rs, mid + 1, R);
PushUp(now);
}
void Change(int now, int L, int R) //区间修改
{
if(Tree[now].Num > 5) return ; //已全为1, 不需修改
if(L <= Tree[now].L && Tree[now].R <= R)
{
Tree[now].Num ++; //更新开方次数
if(Tree[now].L == Tree[now].R) //若为叶节点. 暴力修改
{
Tree[now].Sum = sqrt(Tree[now].Sum);
return ;
}
}
int mid = (Tree[now].L + Tree[now].R) >> 1;
if(L <= mid) Change(ls, L, R);
if(R > mid) Change(rs, L, R);
PushUp(now);
}
ll Query(int now, int L, int R) //查询区间和
{
if(L <= Tree[now].L && Tree[now].R <= R) return Tree[now].Sum;
int mid = (Tree[now].L + Tree[now].R) >> 1;
ll ret = 0;
if(L <= mid) ret += Query(ls, L, R);
if(R > mid) ret += Query(rs, L, R);
return ret;
}
//===========================================================
int main()
{
while(scanf("%d", &N) != EOF)
{
memset(Tree, 0, sizeof(Tree)); printf("Case #%d:\n", ++ T);
for(int i = 1; i <= N; i ++) Original[i] = read();
Build(1, 1, N);
M = read();
while(M --)
{
int opt = (int) read(), x = (int) read(), y = (int) read();
if(x > y) std :: swap(x, y);
if(! opt) Change(1, x, y);
else printf("%lld\n", Query(1, x, y));
}
printf("\n");
}
return 0;
}
GSS - Can you answer these queries I ~ ? (持续更新...)
原文地址:https://www.cnblogs.com/luckyblock/p/12150531.html
时间: 2024-11-07 16:40:25