蒲公英(分块)

题面

在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关。

为了简化起见,我们把所有的蒲公英看成一个长度为 n 的序列a1,a2,…,an,其中ai为一个正整数,表示第 i 棵蒲公英的种类编号。

而每次询问一个区间 [l,r] ,你需要回答区间里出现次数最多的是哪种蒲公英,如果有若干种蒲公英出现次数相同,则输出种类编号最小的那个。

输入格式

第一行两个整数n,m,表示有 n 株蒲公英,m 次询问。

接下来一行 n 个空格隔开的整数ai,表示蒲公英的种类。

再接下来 m 行每行两个整数l0,r0,我们令上次询问的结果为 x(如果这是第一次询问,则 x=0)。

令l=(l0+x-1) mod n+1,r=(r0+x-1) mod n+1,如果l>r,则交换l,r。

最终的询问区间为[l,r]。

输出格式

输出 m 行。

每行一个整数,表示每次询问的结果。

数据范围

1≤n≤40000,
1≤m≤50000,
1≤ai≤109

输入样例:

6 3 
1 2 3 2 1 2 
1 5 
3 6 
1 5

输出样例:

1 
2 
1

题解

先离散化+预处理 O(nt + nlongn), t为总共的分块数, len为块的大小
当 r - l + 1 >= len * 2 - 1, 则l-r内必有一个整块(包含边界)
1.如果不想麻烦,可以对 r - l + 1 < len * 2 - 1 直接暴力,从 l 到到r,按书上二分方法去查找在l-r直接a[i]的个数,暴力找答案
2.l为块左边界 (l % len == 1),r为右边界(r % len == 0 || r == n(最后不足一块)),预处理已经知道答案
3.l为块左边界,找到r 所在块的左边界 rl,预处理知道答案 l-(lr - 1), 暴力rl - r
3.r为块右边界, 同上
4.分别找 rl 和 lr,预处理知道 (lr + 1) - (rl - 1)的答案,暴力l - rl、 rl - r即可

大概复杂度为 O(nt + m *n * log n / t), 求导 t = (m * n * log(n))^ (1/3)

#include <bits/stdc++.h>
#define find(x) ((x - 1) / len + 1)
#define wide(l,r) (r - l + 1)
using namespace std;

const int maxn = 4e4 + 5;
const int maxt = 3130;

int len, t, n, m, a[maxn], b[maxn], f[maxt][maxt], tax[maxn], x;
vector<int> ve[maxn];

void init()
{
    t = (int)sqrt(n * log2(n));
    len = (n - 1) / t + 1;
    sort(b + 1, b + 1 + n);
    int tot = unique(b + 1, b + 1 + n) - b - 1;
    for (int i = 1; i <= n; ++ i)
    {
        a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b;
        ve[a[i]].push_back(i);
    }
    for (int i = 1; i <= t; ++ i, memset(tax, 0, sizeof tax))
    {
        int mid = (i - 1) * len + 1;
        for (int j = (i - 1) * len + 1; j <= n; ++ j)
        {
            ++ tax[a[j]];
            if (mid != a[j])
            {
                if (tax[mid] < tax[a[j]]) mid = a[j];
                else if (tax[mid] == tax[a[j]] && mid > a[j]) mid = a[j];
            }
            if (j % len == 0 || j == n) f[i][find(j)] = mid;
        }
    }
}

int get(int l, int r, int k)
{
    int ll = lower_bound(ve[k].begin(), ve[k].end(), l) - ve[k].begin();
    if (ll == ve[k].size()) return 0;
    return upper_bound(ve[k].begin(), ve[k].end(), r) - ve[k].begin() - ll;
}

int work(int cnt, int l, int r, int ll, int rr)
{
    for (int j = l, res; j <= r; ++ j)
    {
        if (x != a[j] || j == l)
        {
            res = get(ll, rr, a[j]);
            if (cnt < res) cnt = res, x = a[j];
            else if (cnt == res && x > a[j]) x = a[j];
        }
    }
    return cnt;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++ i) scanf("%d", a + i), b[i] = a[i];
    init();
    for (int i = 1, l, r; i <= m; ++ i)
    {
        scanf("%d%d", &l, &r);
        l = (b[x] % n - 1 + l) % n + 1, r = (b[x] % n - 1 + r) % n + 1;
        if (l > r) swap(l, r);
        if (wide(l, r) < len << 1) work(0, l, r, l, r);
        else if (l % len == 1 && (r % len == 0 || r == n)) x = f[find(l)][find(r)];
        else if (l % len == 1)
        {
            int rl = (find(r) - 1) * len + 1;
            x = f[find(l)][find(r) - 1];
            work(get(l, r, x), rl, r, l, r);
        }
        else if (r % len == 0)
        {
            int lr = find(l) * len;
            x = f[find(l) + 1][find(r)];
            work(get(l, r, x), l, lr, l, r);
        }
        else
        {
            int lr = find(l) * len, rl = (find(r) - 1) * len + 1;
            x = f[find(l) + 1][find(r) - 1];
            work(work(get(l, r, x), l, lr, l, r), rl, r, l, r);
        }
        printf("%d\n", b[x]);
    }
    return 0;
}
posted @ 2020-05-25 11:07  洛绫璃  阅读(259)  评论(0编辑  收藏  举报