Acwing 4339 敌兵布阵 暴力 + 分块 + 线段树 + Zkw线段树 + 树状数组

来一篇超全题解

数据结构大杂烩

原题连接

题目描述

敌人有 N N N 个工兵营地,编号 1 ∼ N 1∼N 1N

初始时,第 i i i 个营地有 a i a_i ai 个人。

接下来有若干个命令,命令有 4 4 4 种形式:

Add i j i i i j j j 为正整数,表示第 i i i 个营地增加 j j j 个人。( j j j 不超过 30 30 30
Sub i j i i i j j j 为正整数,表示第 i i i 个营地减少 j j j 个人。( j j j 不超过 30 30 30
Query i j i i i j j j 为正整数 ( i ≤ j ) (i≤j) ij,表示询问第 i i i 到第 j j j 个营地的总人数。
End,表示结束,此命令只会作为最后一条命令出现。
请你计算每个 Query 的答案。

输入格式
第一行包含整数 T T T,表示共有 T T T 组测试数据。

每组数据第一行包含一个整数 N N N

第二行包含 N N N 个整数 a 1 , a 2 , … , a N a1,a2,…,aN a1,a2,,aN

接下来若干行,每行包含一条命令,格式如题目所述。

输出格式
对于第 i i i 组数据,首先输出一行 Case i:,然后对于每个 Query 询问,输出一行一个整数,表示询问的段中的总人数。

数据范围

1 ≤ T ≤ 10 , 1≤T≤10, 1T10,
1 ≤ N ≤ 50000 , 1≤N≤50000, 1N50000,
1 ≤ a i ≤ 50 , 1≤ai≤50, 1ai50,
每组数据最多有 40000 40000 40000 条命令,
保证任何营地的人数都不会减少为负数。

输入样例:

1
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End

输出样例:

Case 1:
6
33
59

Code

暴力 + 快读快写 + 吸氧 T L 90 p t s TL 90pts TL90pts

#include <cstdio>

#pragma GCC target ("avx") 
#pragma GCC optimize (2, 3, "Ofast", "inline", "-ffast-math")

const int N = 5e4 + 10;

int n, a[N];

namespace IO
{
    template <class T> inline void read (T &x) { x = 0; bool f = 0; char ch = getchar (); while (ch < '0' || ch > '9') f |= ch == '-', ch = getchar (); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar (); x = f ? ~x + 1 : x; }
    template <class T> inline void write (T x) { static char c[20]; unsigned p = 0; if (x < 0) putchar ('-'), x = ~x + 1; if (!x) { putchar ('0'); return ; } while (x) c[++p] = x % 10 ^ 48, x /= 10; while (p) putchar (c[p]), --p; }
    template <class T, class... U> inline void read (T &x, U &...t) { read (x), read (t...); }
    template <class T, class... U> inline void write (T x, U ...t) { write (x), write (t...); }
}

using namespace IO;

int main ()
{
    int _; read (_);
    for (int T = 1; T <= _; ++T)
    {
        read (n);
        for (int i = 1; i <= n; ++i) read (a[i]);
        
        printf ("Case %d:\n", T);
        while (true)
        {
            char opt[5];
            int x, y;
            
            scanf ("%s", opt);
            if (*opt == 'A') read (x, y), a[x] += y;
            if (*opt == 'S') read (x, y), a[x] -= y;
            if (*opt == 'Q')
            {
                read (x, y);
                int res = 0;
                for (int i = x; i <= y; ++i) res += a[i];
                write (res), putchar ('\n');
            }
            if (*opt == 'E') break;
        }
    }
    return 0;
}

优雅的暴力永不过时~~

分块 A C AC AC 1194 m s 1194ms 1194ms

O ( m n ) O(m \sqrt{n}) O(mn )

#include <cstdio>
#include <cmath>
#include <cstring>

#pragma GCC target ("avx")
#pragma GCC optimize (2, 3, "Ofast", "inline", "-ffast-math")

const int N = 5e4 + 10, M = 230;

int n, m, unit, w[N], pos[N], sum[M];

inline void update (int k, int x)
{
    w[k] += x, sum[pos[k]] += x;
}

inline int query (int l, int r)
{
    int res = 0;
    if (pos[l] == pos[r])
        for (int k = l; k <= r; ++k) res += w[k];
    else
    {
        int i = l, j = r;
        while (pos[i] == pos[l]) res += w[i], ++i;
        while (pos[j] == pos[r]) res += w[j], --j;
        for (int k = pos[i]; k <= pos[j]; ++k) res += sum[k];
    }
    return res;
}

namespace IO
{
    template <class T> inline void read (T &x) { x = 0; bool f = 0; char ch = getchar (); while (ch < '0' || ch > '9') f |= ch == '-', ch = getchar (); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar (); x = f ? ~x + 1 : x; }
    template <class T> inline void write (T x) { static char c[20]; unsigned p = 0; if (x < 0) putchar ('-'), x = ~x + 1; if (!x) { putchar ('0'); return ; } while (x) c[++p] = x % 10 ^ 48, x /= 10; while (p) putchar (c[p]), --p; }
    template <class T, class... U> inline void read (T &x, U &...t) { read (x), read (t...); }
    template <class T, class... U> inline void write (T x, U ...t) { write (x), write (t...); }
}

using namespace IO;

inline void init ()
{
    unit = sqrt (n);
    for (int i = 1; i <= n; ++i) read (w[i]), sum[pos[i] = i / unit] += w[i];
}

int main ()
{
    int _; read (_);
    
    for (int T = 1; T <= _; ++T)
    {
        memset (sum, 0, sizeof sum);
        
        read (n);
        init ();
        
        printf ("Case %d:\n", T);
        while (true)
        {
            char opt[5];
            int x, y;
            scanf ("%s", opt);
            
            if (*opt == 'A') read (x, y), update (x, y);
            if (*opt == 'S') read (x, y), update (x, -y);
            if (*opt == 'Q') read (x, y), write (query (x, y)), putchar ('\n');
            if (*opt == 'E') break;
        }
    }
    return 0;
}

大杀器

线段树 A C AC AC 1868 m s 1868ms 1868ms

O ( m log ⁡ 2 n ) O(m \log_2{n}) O(mlog2n)

#include <cstdio>

#pragma GCC target ("avx")
#pragma GCC optimize (2, 3, "Ofast", "inline", "-ffast-math")

const int N = 5e4 + 10;

int n, a[N], tr[N << 2], tag[N << 2];

inline void push_up (int x)
{
    tr[x] = tr[x << 1] + tr[x << 1 | 1];   
}

inline void push_down (int mid, int l, int r, int x)
{
    tr[x << 1] += tag[x] * (mid - l + 1);
    tr[x << 1 | 1] += tag[x] * (r - mid);
    tag[x << 1] += tag[x], tag[x << 1 | 1] += tag[x], tag[x] = 0;
}

inline void build (int l = 1, int r = n, int x = 1)
{
    if (l == r) tr[x] = a[l];
    else
    {
        int mid = l + r >> 1;
        build (l, mid, x << 1), build (mid + 1, r, x << 1 | 1);
        push_up (x);
    }
}

inline void update (int l, int r, int d, int nl = 1, int nr = n, int x = 1)
{
    if (l > nr || r < nl) return ;
    if (l <= nl && r >= nr) tag[x] += d, tr[x] += d * (nr - nl + 1);
    else
    {
        int mid = nl + nr >> 1;
        push_down (mid, nl, nr, x);
        update (l, r, d, nl, mid, x << 1), update (l, r, d, mid + 1, nr, x << 1 | 1);
        push_up (x);
    }
}

inline int query (int l, int r, int nl = 1, int nr = n, int x = 1)
{
    if (l > nr || r < nl) return 0;
    if (l <= nl && r >= nr) return tr[x];
    else
    {
        int mid = nl + nr >> 1;
        push_down (mid, nl, nr, x);
        return query (l, r, nl, mid, x << 1) + query (l, r, mid + 1, nr, x << 1 | 1);
    }
}

namespace IO
{
    template <class T> inline void read (T &x) { x = 0; bool f = 0; char ch = getchar (); while (ch < '0' || ch > '9') f |= ch == '-', ch = getchar (); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar (); x = f ? ~x + 1 : x; }
    template <class T> inline void write (T x) { static char c[20]; unsigned p = 0; if (x < 0) putchar ('-'), x = ~x + 1; if (!x) { putchar ('0'); return ; } while (x) c[++p] = x % 10 ^ 48, x /= 10; while (p) putchar (c[p]), --p; }
    template <class T, class... U> inline void read (T &x, U &...t) { read (x), read (t...); }
    template <class T, class... U> inline void write (T x, U ...t) { write (x), write (t...); }
}

using namespace IO;

int main ()
{
    int _; read (_);
    
    for (int T = 1; T <= _; ++T)
    {
        read (n);
        for (int i = 1; i <= n; ++i) read (a[i]);
        
        build ();
        printf ("Case %d:\n", T);
        while (true)
        {
            char opt[5];
            int x, y;
            scanf ("%s", opt);
            
            if (*opt == 'A') read (x, y), update (x, x, y);
            if (*opt == 'S') read (x, y), update (x, x, -y);
            if (*opt == 'Q') read (x, y), write (query (x, y)), putchar ('\n');
            if (*opt == 'E') break;
        }
    }
    return 0;
}

常数稍小的线段树

Z k w Zkw Zkw线段树 A C AC AC 1158 m s 1158ms 1158ms

O ( m log ⁡ 2 n ) O(m \log_2{n}) O(mlog2n)

#include <cstdio>
#include <cstring>

#pragma GCC target ("avx")
#pragma GCC optimize (2, 3, "Ofast", "inline", "-ffast-math")

const int N = 5e4 + 10;

int n, M, a[N], tr[N << 2], tag[N << 2];

inline void update (int s, int t, int d)
{
    int ln = 0, rn = 0, nn = 1;
    for (s += M - 1, t += M + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nn <<= 1)
    {
        tr[s] += d * ln, tr[t] += d * rn;
        if (~s & 1) tag[s ^ 1] += d, tr[s ^ 1] += d * nn, ln += nn;
        if (t & 1) tag[t ^ 1] += d, tr[t ^ 1] += d * nn, rn += nn;
    }
    for (; s; s >>= 1, t >>= 1) tr[s] += d * ln, tr[t] += d * rn;
}

inline int query (int s, int t)
{
    int ans = 0, ln = 0, rn = 0, nn = 1;
    for (s += M - 1, t += M + 1; s ^ t ^ 1; s >>= 1, t >>= 1, nn <<= 1)
    {
        if (tag[s]) ans += tag[s] * ln;
        if (tag[t]) ans += tag[t] * rn;
        if (~s & 1) ans += tr[s ^ 1], ln += nn;
        if (t & 1) ans += tr[t ^ 1], rn += nn;
    }
    for (; s; s >>= 1, t >>= 1) ans += tag[s] * ln, ans += tag[t] * rn;
    return ans;
}

namespace IO
{
    template <class T> inline void read (T &x) { x = 0; bool f = 0; char ch = getchar (); while (ch < '0' || ch > '9') f |= ch == '-', ch = getchar (); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar (); x = f ? ~x + 1 : x; }
    template <class T> inline void write (T x) { static char c[20]; unsigned p = 0; if (x < 0) putchar ('-'), x = ~x + 1; if (!x) { putchar ('0'); return ; } while (x) c[++p] = x % 10 ^ 48, x /= 10; while (p) putchar (c[p]), --p; }
    template <class T, class... U> inline void read (T &x, U &...t) { read (x), read (t...); }
    template <class T, class... U> inline void write (T x, U ...t) { write (x), write (t...); }
}

using namespace IO;

inline void init ()
{
    M = 1;
    while (M <= n + 1) M <<= 1;
    for (int i = 1; i <= n; ++i) read (tr[M + i]);
    for (int i = M - 1; i; --i) tr[i] = tr[i << 1] + tr[i << 1 | 1];
}

int main ()
{
    int _; read (_);
    
    for (int T = 1; T <= _; ++T)
    {
        read (n);
        init ();
        
        printf ("Case %d:\n", T);
        while (true)
        {
            char opt[5];
            int x, y;
            
            scanf ("%s", opt);
            if (*opt == 'A') read (x, y), update (x, x, y);
            if (*opt == 'S') read (x, y), update (x, x, -y);
            if (*opt == 'Q') read (x, y), write (query (x, y)), putchar ('\n');
            if (*opt == 'E') break;
        }
    }
    return 0;
}

树状数组 A C AC AC 890 m s 890ms 890ms

O ( m log ⁡ 2 n ) O(m \log_2{n}) O(mlog2n)

#include <cstdio>
#include <cstring>

#pragma GCC target ("avx")
#pragma GCC optimize (2, 3, "Ofast", "inline", "-ffast-math")

#define lowbit(x) ((x) & ~(x) + 1)

const int N = 5e4 + 10;

int n, tr[N];

inline void update (int k, int x)
{
    while (k <= n) tr[k] += x, k += lowbit (k);
}

inline int query (int k)
{
    int ans = 0;
    while (k) ans += tr[k], k -= lowbit (k);
    return ans;
}

namespace IO
{
    template <class T> inline void read (T &x) { x = 0; bool f = 0; char ch = getchar (); while (ch < '0' || ch > '9') f |= ch == '-', ch = getchar (); while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar (); x = f ? ~x + 1 : x; }
    template <class T> inline void write (T x) { static char c[20]; unsigned p = 0; if (x < 0) putchar ('-'), x = ~x + 1; if (!x) { putchar ('0'); return ; } while (x) c[++p] = x % 10 ^ 48, x /= 10; while (p) putchar (c[p]), --p; }
    template <class T, class... U> inline void read (T &x, U &...t) { read (x), read (t...); }
    template <class T, class... U> inline void write (T x, U ...t) { write (x), write (t...); }
}

using namespace IO;

int main ()
{
    int _; read (_);
    
    for (int T = 1; T <= _; ++T)
    {
        memset (tr, 0, sizeof tr);
        
        read (n);
        for (int i = 1, x; i <= n; ++i)
        {
            read (x), tr[i] += x;
            if (i + lowbit (i) <= n) tr[i + lowbit (i)] += tr[i];
        }
        
        printf ("Case %d:\n", T);
        while (true)
        {
            char opt[5];
            int x, y;
            scanf ("%s", opt);
            
            if (*opt == 'A') read (x, y), update (x, y);
            if (*opt == 'S') read (x, y), update (x, -y);
            if (*opt == 'Q') read (x, y), write (query (y) - query (x - 1)), putchar ('\n');
            if (*opt == 'E') break;
        }
    }
    return 0;
}

总结:

想题目中这样的数据范围,暴力一般都是过不了的,我们自然而然地就想到了上述的这些 O ( m n ) O(m\sqrt{n}) O(mn ) O ( m log ⁡ 2 n ) O(m\log_2{n}) O(mlog2n)的数据结构。只要把模板打熟,以后遇到这样的题就不怕了~~

制作不易,请一键三连~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值