【BZOJ2821】作诗(Poetize)

【题目链接】

【前置技能】

  • 分块

【题解】

  • 题意:有一个长度为 N 的序列 A ,其中 A i C M 组询问,每次询问区间 [ L , R ] 中出现正偶数次的数的种类数,强制在线。
  • 数据范围: N , M , C 10 5
  • 首先,这道题不强制在线怎么做呢?莫队。
  • 似乎强制在线之后就不太好做了,怎么办呢?分块大法好。
  • 先预处理出每一段连续的整块的答案,即整块 i j 以及之间的整块的答案。枚举最左边的整块,暴力统计个数和答案即可。还要预处理出到每一个整块的末尾处每个数有多少个。从头开始统计,到块的末尾处记下数组即可。
  • 每次询问的时候,若 L R 在同一块中,暴力处理即可,注意数组不能memset。若不在同一块中,我们先将答案记为中间整块的答案,对于旁边的散块,将其中的数记下来,统计一下每种数的个数。利用我们预处理出的第二个数组求出整块内每种数的个数,结合散块中的每种数的个数,对答案进行调整。
  • N , M , C 同阶的情况下,时间复杂度 O ( N N K + N K ) ,当 K N 时为 O ( N N )
  • 本题这种做法的空间卡得有点紧,数组不能多开太多。

【代码】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL  long long
#define MAXN    100010
#define MAXM    320
using namespace std;
int belong[MAXN], lb[MAXM], rb[MAXM];
int n, m, c, lastans, k, tot, w[MAXN], a[MAXM][MAXM], cnt[MAXN], b[MAXM][MAXN], tmp[MAXM * 2], cmt;
vector <int> used;

template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
    x = 0; int f = 1; char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
    while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}

int main(){
    read(n), read(c), read(m);
    for (int i = 1; i <= n; ++i)
        read(w[i]);
    k = (int)(sqrt(n) + 0.5);
    for (int i = 1; i <= n; ++i){
        if (i % k == 1) ++tot, lb[tot] = i, rb[tot - 1] = i - 1;
        belong[i] = tot;
    }
    rb[tot] = n;
    for (int i = 1; i <= tot; ++i){
        memset(cnt, 0, sizeof(cnt));
        int ans = 0;
        for (int j = i; j <= tot; ++j){
            for (int t = lb[j]; t <= rb[j]; ++t){
                ++cnt[w[t]];
                if (cnt[w[t]] == 1) continue;
                if (cnt[w[t]] % 2 == 0) ++ans; else --ans;
            }
            a[i][j] = ans;
        }
    }
    memset(cnt, 0, sizeof(cnt));
    for (int i = 1; i <= tot; ++i){
        for (int j = lb[i]; j <= rb[i]; ++j)
            ++cnt[w[j]];
        for (int j = 1; j <= c; ++j)
            b[i][j] = cnt[j];
    }
    memset(cnt, 0, sizeof(cnt));
    while (m--){
        int l, r; read(l), read(r);
        l = (l + lastans) % n + 1, r = (r + lastans) % n + 1;
        if (l > r) swap(l, r);
        lastans = 0;
        if (r - l + 1 < 2 * k) {
            for (int i = l; i <= r; ++i){
                ++cnt[w[i]];
                if (cnt[w[i]] == 1) continue;
                if (cnt[w[i]] % 2 == 0) ++lastans; else --lastans;
            }
            for (int i = l; i <= r; ++i)
                --cnt[w[i]];
        } else {
            int L = belong[l], R = belong[r];
            if (l != lb[L]) ++L; if (r != rb[R]) --R;
            lastans = a[L][R];
            cmt = 0;
            for (int i = l; i < lb[L]; ++i)
                tmp[++cmt] = w[i];
            for (int i = rb[R] + 1; i <= r; ++i)
                tmp[++cmt] = w[i];
            for (int i = 1; i <= cmt; ++i){
                if (!cnt[tmp[i]]) used.push_back(tmp[i]);
                ++cnt[tmp[i]];
            }
            for (int i = 0, si = used.size(); i < si; ++i){
                int x = cnt[used[i]], y = b[R][used[i]] - b[L - 1][used[i]]; 
                if (y == 0){
                    if (x % 2 == 0) ++lastans;
                } else {
                    if (x % 2 == 1){
                        if (y % 2 == 0) --lastans;
                        else ++lastans;
                    }
                }
            }
            for (int i = 1; i <= cmt; ++i)
                --cnt[tmp[i]];
            used.clear();
        }
        printf("%d\n", lastans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/six_solitude/article/details/80556543
今日推荐