1. 程式人生 > >SPOJ-DQUERY D-query 莫隊演算法

SPOJ-DQUERY D-query 莫隊演算法

題意: 給定一個區間和2e5次查詢, 查詢區間[L, R]的不同的數的個數. 分析: 莫隊和主席樹都可以解決, 先離線再處理查詢. 1. 莫隊演算法: 莫隊演算法一般用來處理區間查詢問題, 區間必須離線, 更新操作要簡單的問題. 主要做法是將區間分塊, 然後根據區間的範圍進行排序, 查詢R節點為第一關鍵字, L節點為第二關鍵字. 排序後在進行處理, 算是一種優美的暴力, 莫隊演算法在處理離線區間查詢問題幾乎是無敵的. 對於這道題, 查詢的是不同數的個數, 離線處理時就直接記錄不同的數的個數就好, 算是一道模板題. 2. 主席樹: 待更新. 莫隊演算法程式碼:

#include <bits/stdc++.h>

using namespace std;
const int MAXN = 1e6 + 10;
struct Query
{
    int l, r, id;
} Q[MAXN];

int n, m;
int L = 1, R = 0, Ans = 0;
int pos[MAXN], a[MAXN];
int flag[MAXN], ans[MAXN];
map<int, int> mp;
bool cmp(Query x, Query y)
{
    if (pos[x.l] == pos[y.l])
        return x.r < y.r;
    else
        return pos[x.l] < pos[y.l];
}

void add(int x)
{
    if (flag[a[x]] == 0)
        Ans++;
    flag[a[x]]++;
}
void del(int x)
{
    flag[a[x]]--;
    if (flag[a[x]] == 0)
        Ans--;
}
int main()
{
    scanf("%d", &n);
    int unit = sqrt(n);
    int sz = 0;
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        if (mp[a[i]] == 0)
        {
            mp[a[i]] = ++sz;
        }
        a[i] = mp[a[i]];
        pos[i] = i / unit + 1;
    }
    scanf("%d", &m);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d", &Q[i].l, &Q[i].r);
        Q[i].id = i;
    }

    sort(Q + 1, Q + 1 + m, cmp);

    for (int i = 1; i <= m; i++)
    {
        while (L < Q[i].l)
        {
            del(L);
            L++;
        }
        while (L > Q[i].l)
        {
            L--;
            add(L);
        }
        while (R < Q[i].r)
        {
            R++;
            add(R);
        }
        while (R > Q[i].r)
        {
            del(R);
            R--;
        }
        ans[Q[i].id] = Ans;
    }

    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    return 0;
}

主席樹程式碼:

//待更新