CF 1000F One Occurrence

时光如梭,Codeforces 的题号还是三位数的时代已经过去;量子语言原来已经来临。

传送门
题目大意

给定一个长度为 n 的序列 a ,有 m 个询问,每次询问给定一个区间 [ l , r ] ,如果这个区间里存在恰好只出现一次的数,输出这个数(如果有多个就任意输出一个),否则输出 0

n , m 5 × 10 5 a i 10 5

思路

还好题解是英文的看不懂啊哈哈哈。

首先题解中有三个类型:主席树,线段树,基于莫队的算法(Mo-based algorithm,出题人不是中国的,真神仙),然后我瞄了第一句话:如果所有询问右端点一样……然后我就自己想了……

不妨求出每个位置下一个与它相同的数所在的位置, O ( n ) 扫描即可,我们用一个从前往后的箭头表示这个东西。发现,如果区间内存在恰好只出现一次的数,那么这个数必须满足两个条件:指向它的箭头不存在或者起点在左端点外;它指向其它位置的箭头在右端点外(如果没有,不妨令这个箭头指向 n + 1 )。

假设一个区间内所有位置都不存在指向它的箭头,怎么判断这个区间是否有解呢?直接取这个区间内指向别的地方的位置的最大值,拿它和右端点比较即可,这个可以用线段树维护。现在有些位置有箭头指向它,换句话说,有些位置在求最大值时不能算进对答案的贡献中。

考虑把询问按左端点离线,从左到右处理。对于有箭头指向它的位置,我们让它的箭头指向的位置减去一个极大值,然后在区间中取最大值即可。当处理完一个左端点后,如果左端点指向了某个位置,我们就把那个位置指向的位置加上之前减去的那个极大值,这样就能正确统计答案了。用线段树维护,时间复杂度 O ( n log n )

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
typedef long long LL;
typedef unsigned long long ULL;
using std::cin;
using std::cout;
using std::endl;
typedef int INT_PUT;
INT_PUT readIn()
{
    INT_PUT a = 0; bool positive = true;
    char ch = getchar();
    while (!(ch == '-' || std::isdigit(ch))) ch = getchar();
    if (ch == '-') { positive = false; ch = getchar(); }
    while (std::isdigit(ch)) { a = a * 10 - (ch - '0'); ch = getchar(); }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20]; int length = 0;
    if (x < 0) putchar('-'); else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar('\n');
}

const int INF = (~(int(1) << (sizeof(int) * 8 - 1))) >> 1;
const int maxn = int(5e5) + 5;
int n, m;
int a[maxn];
struct Query
{
    int l, r;
    void read()
    {
        l = readIn();
        r = readIn();
    }
} querys[maxn];
std::vector<int> offline[maxn];

int appear[maxn];
int next[maxn];
bool pointer[maxn];
class SegTree
{
    static inline int code(int l, int r)
    {
        return (l + r) | (l != r);
    }
    struct Node
    {
        int maxVal;
        int maxIdx;
        Node() : maxVal(-INF - 1) {}
        bool operator<(const Node& b) const
        {
            return maxVal < b.maxVal;
        }
    } nodes[maxn * 2];

    int g_Pos, g_Val, g_L, g_R;
    void update(int l, int r)
    {
        Node& t = nodes[code(l, r)];
        int mid = (l + r) >> 1;
        Node& lc = nodes[code(l, mid)];
        Node& rc = nodes[code(mid + 1, r)];
        if (lc.maxVal >= rc.maxVal)
            t = lc;
        else
            t = rc;
    }
    void add_(int l, int r)
    {
        if (l == r)
        {
            nodes[code(l, r)].maxVal += g_Val;
            return;
        }
        int mid = (l + r) >> 1;
        if (g_Pos <= mid) add_(l, mid);
        else add_(mid + 1, r);
        update(l, r);
    }
    Node query_(int l, int r)
    {
        if (g_L <= l && r <= g_R)
        {
            return nodes[code(l, r)];
        }
        int mid = (l + r) >> 1;
        Node ret;
        if (g_L <= mid)
            ret = std::max(ret, query_(l, mid));
        if (g_R > mid)
            ret = std::max(ret, query_(mid + 1, r));
        return ret;
    }

public:
    void build(int l, int r)
    {
        if (l == r)
        {
            Node& t = nodes[code(l, r)];
            t.maxVal = -INF + next[l];
            t.maxIdx = a[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(l, mid);
        build(mid + 1, r);
        update(l, r);
    }
    void add(int pos, int val)
    {
        g_Pos = pos;
        g_Val = val;
        add_(1, n);
    }
    int query(int l, int r)
    {
        g_L = l;
        g_R = r;
        Node t = query_(1, n);
        if (t.maxVal > r)
            return t.maxIdx;
        else
            return 0;
    }
} st;

int ans[maxn];

void run()
{
    n = readIn();
    for (int i = 1; i <= n; i++)
        a[i] = readIn();
    m = readIn();
    for (int i = 1; i <= m; i++)
        querys[i].read();

    for (int i = 1; i <= m; i++)
        offline[querys[i].l].push_back(i);

    for (int i = n; i; i--)
    {
        next[i] = appear[a[i]];
        if (!next[i]) next[i] = n + 1;
        appear[a[i]] = i;
    }
    for (int i = 1; i <= n; i++)
        if (next[i])
            pointer[next[i]] = true;
    st.build(1, n);
    for (int i = 1; i <= n; i++)
        if (!pointer[i])
            st.add(i, INF);

    for (int l = 1; l <= n; l++)
    {
        for (int i = 0; i < offline[l].size(); i++)
        {
            int idx = offline[l][i];
            int r = querys[idx].r;
            ans[idx] = st.query(l, r);
        }
        if (next[l] <= n)
            st.add(next[l], INF);
    }
    for (int i = 1; i <= m; i++)
        printOut(ans[i]);
}

int main()
{
    run();
    return 0;
}
总结

要是一眼题解都不看就会做就好了。唉,谁叫我太弱了呢 QAQ。

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80946588