P1972 [SDOI2009]HH的项链

第一道莫队,日常抄题解。。

luogu上面数据加强了,最后两个点莫队跑不过是最骚的。

但是不妨碍我写随笔。。。

莫队算法思想就是给你两个神奇的指针l和r,然后通过排序使得他们尽量为\(O(1)\)地跳完整个答案。

显然是个离线算法,但是只要搞出来就ok了呀。

代码参考的是Menci大佬的:https://oi.men.ci/sdoi2009-diff/

话说两次排序跟一次排序用的时间是差不多的?

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
const int maxn = 500005;
const int maxN = 1005;
const int maxm = 1000005;

struct Nodes
{
    int l, r, idx;
} q[maxn];
int a[maxn];
int belong[maxn], ll[maxN], rr[maxN];
int len, num;
int cnt[maxm];
int ans[maxn];
int n, m;
int l = 1, r = 0, sum = 0;
int read()
{
    int ans = 0, s = 1;
    char ch = getchar();
    while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
    while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
    return s * ans;
}
bool cmp1(const Nodes x, const Nodes y)
{
    if(belong[x.l] == belong[y.l]) return x.r < y.r;
    return belong[x.l] < belong[y.l];
}
bool cmp2(const Nodes x, const Nodes y)
{
    return x.idx < y.idx;
}
void init()
{
    len = sqrt(n);
    for(int i = 1; i <= m; i++) belong[i] = (i - 1) / len + 1;
}
void add(int x)
{
    x = a[x];
    if(cnt[x] == 0) sum++;
    cnt[x]++;
}
void del(int x)
{
    x = a[x];
    if(cnt[x] == 1) sum--;
    cnt[x]--;
}
int extend(int l, int r, bool right, bool add)//大佬说这里是莫队修改模板
{
    if(right)
    {
        if(add)
        {
            cnt[a[r]]++;
            if(cnt[a[r]] == 1) return 1;
            else return 0;
        }
        else
        {
            cnt[a[r]]--;
            if(cnt[a[r]] == 0) return -1;
            else return 0;
        }
    }
    else
    {
        if(add)
        {
            cnt[a[l]]++;
            if(cnt[a[l]] == 1) return 1;
            else return 0;
        }
        else
        {
            cnt[a[l]]--;
            if(cnt[a[l]] == 0) return -1;
            else return 0;
        }
    }
}
void solve()//莫队算法核心
{
    for(int i = 1; i <= m; i++)
    {
        int ql = q[i].l, qr = q[i].r;
        while(r < qr) sum += extend(l, ++r, 1, 1);
        while(r > qr) sum += extend(l, r--, 1, 0);
        while(l > ql) sum += extend(--l, r, 0, 1);//r已经到达目的地,向左跳答案要加
        while(l < ql) sum += extend(l++, r, 0, 0);//同理要减
        ans[q[i].idx] = sum;
    }
}
int main()
{
    n = read();
    for(int i = 1; i <= n; i++) a[i] = read();
    m = read();
    for(int i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].idx = i;
    init();
    std::sort(q + 1, q + m + 1, cmp1);
    solve();
    for(int i = 1; i <= m; i++) printf("%d\n", ans[i]);
    return 0;
}
posted @ 2018-09-17 15:16  Garen-Wang  阅读(130)  评论(0编辑  收藏  举报