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

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-08-30 12:34:37

GSS - Can you answer these queries I ~ ? (持续更新...)的相关文章

H - Can you answer these queries? - (区间查询更新)

有一列数,(都是2^63范围内的并且都大于0的整数),现在呢有一些操作, 操作 0 可以把区间LR内的所有数都变成它的平方根数(是取整后的),操作 1 可以就是求区间LR内的和了. 分析:因为这个操作是把一个数变成平方根,所以显得略棘手,不过如果仔细演算的话会发现一个2^64数的平方根开8次也就变成了 1,所以也更新不了多少次,所以可以每次更新到底.. 注意:给的X Y大小未知,会出现X > Y的情况 ***********************************************

HDU4027 Can you answer these queries 线段树区间求和+剪枝

给了你n,然后n个数字在一个数组中,接下来m个询问,每个询问三个数字 t,x,y,若t==0,那么修改区间[x,y]的每一个值,变为原来每个位置上的数 开根号取整,若t==1,那么对区间[x,y]求和 由于n,m,很大,所以树状数组铁定超时,若直接用线段树来做区间修改,那么也是超时,这类题目没别的方法了,静心剪枝,发现题目给的数据范围为2^63,有没有发现,2^63开根号 绝对不需要开10次,就能到1,到1以后就不需要再开了,意思就是若有某个区间[x,y]每一个点的值都为1时,这一段区间事实上是

hdu4027 Can you answer these queries?

Problem Description A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of ou

bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

2482: [Spoj1557] Can you answer these queries II Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 145  Solved: 76[Submit][Status][Discuss] Description 给定n个元素的序列. 给出m个询问:求l[i]~r[i]的最大子段和(可选空子段). 这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次. 比如:1,2,3,2,2,2出现了3次,但只算一次,

HDU 4027 Can you answer these queries(线段树 成段更新)

Problem Description A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of ou

SPOJ 1043 Can you answer these queries I 求任意区间最大连续子段和 线段树

题目链接:点击打开链接 维护区间左起连续的最大和,右起连续的和.. #include <cstdio> #include <iostream> #include <algorithm> #include <string.h> #include <math.h> #include <vector> #include <map> using namespace std; #define N 50050 #define Lson

HDU 4027 Can you answer these queries? (线段树+区间点修改)

题意:给你 n 个数,m个询问(c,x,y) c==0 把x,y区间的值变为原来的平方根(向下取整) c==1 计算x,y区间的和. 利用1的开方永远为1剪枝.. #include<cstdio> #include<stdlib.h> #include<string.h> #include<string> //#include<map> #include<cmath> #include<iostream> #include

HDU 4027 Can you answer these queries?(线段树,区间更新,区间查询)

题目 线段树 简单题意: 区间(单点?)更新,区间求和 更新是区间内的数开根号并向下取整 这道题不用延迟操作 //注意: //1:查询时的区间端点可能前面的比后面的大: //2:优化:因为每次更新都是开平方,同一个数更新有限次数就一直是1了,所以可以这样优化 #include <stdio.h> #include<math.h> #define N 100010 #define LL __int64 #define lson l,m,rt<<1 #define rson

HDU 4027 Can you answer these queries? (线段树区间求和)

Can you answer these queries? Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65768/65768 K (Java/Others)Total Submission(s): 12290    Accepted Submission(s): 2912 Problem Description A lot of battleships of evil are arranged in a line before