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

GSS - Can you answer these queries I ~ ? (持续更新...)

时间:2020-01-04 21:58:09      阅读:83      评论:0      收藏:0      [点我收藏+]

标签:怎么   pos   pre   limits   个数   cpp   动态   set   最大   

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 ~ ? (持续更新...)

标签:怎么   pos   pre   limits   个数   cpp   动态   set   最大   

原文地址:https://www.cnblogs.com/luckyblock/p/12150531.html

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