BZOJ 3922 Karin的弹幕 解题报告

我们考虑分块。

我们设置一个 E ,表示我们要维护 E 个线段树。

i 个线段树按照这样的顺序维护元素:

1,1+i,1+2i,,2,2+i,

于是对于每个 dE 的询问,我们就可以直接在第 d 个线段树里边求区间最大值。

对于 d>E 的询问,我们就暴力每次加 d 地一个一个更新最大值。

每次修改的复杂度是 O(Elogn) 的,

每次查询的复杂度是 O(logn) 或者 O(nE) 的。

我们考虑设置 E 的大小使得 Elogn+nE 最小。

根据基本不等式,可得:当 E=nlogn 的时候,

Elogn=nlogn=nE

所以整个问题的时间复杂度就是 O(nnlogn) 的了。

然而理论上是这样,实际上令 E=min(nlogn,10) 的时候效果最好。

(不信可以看我的提交记录。。。最快的那次就是这么设置的。。。)

毕竟 Gromah 太弱,只会做水题。

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 70000 + 5
#define S 100 + 5
#define M 262144 + 5
#define INF 0x7fffffff
#define ls(x) x << 1
#define rs(x) x << 1 | 1

int n, m, K, A[N], Tree[S + 1][M], Pos[S + 1][M];

inline void Modify(int k, int x, int l, int r, int t, int d)
{
    if (l == r)
    {
        Tree[k][x] += d;
        return ;
    }
    int mid = l + r >> 1;
    if (t <= mid) Modify(k, ls(x), l, mid, t, d);
        else Modify(k, rs(x), mid + 1, r, t, d);
    Tree[k][x] = max(Tree[k][ls(x)], Tree[k][rs(x)]);
}

inline int Query(int k, int x, int l, int r, int s, int t)
{
    if (l == s && r == t)
        return Tree[k][x];
    int mid = l + r >> 1;
    if (t <= mid) return Query(k, ls(x), l, mid, s, t);
        else if (s > mid) return Query(k, rs(x), mid + 1, r, s, t);
        else return max(Query(k, ls(x), l, mid, s, mid), Query(k, rs(x), mid + 1, r, mid + 1, t));
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("3922.in", "r", stdin);
        freopen("3922.out", "w", stdout);
    #endif

    scanf("%d", &n);
    K = (int) sqrt((double) n / log2(n));
    K = min(K, 10);                         //亲测这样子跑得最快。
    for (int i = 1; i <= n; i ++)
        scanf("%d", A + i);
    for (int i = 1; i <= K; i ++)
    {
        int cnt = 0;
        for (int j = 1; j <= i; j ++)
            for (int k = 0; j + k * i <= n; k ++)
            {
                Pos[i][j + k * i] = ++ cnt;
                Modify(i, 1, 1, n, cnt, A[j + k * i]);
            }
    }
    scanf("%d", &m);
    while (m --)
    {
        int op, u, v;
        scanf("%d%d%d", &op, &u, &v);
        if (op == 0)
        {
            for (int i = 1; i <= K; i ++)
                Modify(i, 1, 1, n, Pos[i][u], v);
            A[u] += v;
        }
        else if (op == 1)
        {
            if (v <= K)
            {
                int l = Pos[v][u], r = Pos[v][u + (n - u) / v * v];
                int res = Query(v, 1, 1, n, l, r);
                printf("%d\n", res);
            }
            else
            {
                int res = -INF;
                for (; u <= n; u += v)
                    res = max(res, A[u]);
                printf("%d\n", res);
            }
        }
    }

    #ifndef ONLINE_JUDGE
        fclose(stdin);
        fclose(stdout);
    #endif
    return 0;
}

你可能感兴趣的:(OI,乱搞,分块,bzoj)