Loading

题解 【CF848C Goodbye Souvenir】

\(\huge\texttt{CF848C}\)

前置:cdq分治,\(\texttt{P4390}\) (即cdq分治解决单点修改,矩阵查询的问题)。

此题便是 \(\texttt{P4390}\) 的巧妙转换。

题意

不做赘述。

但是为了更好地做题,题中:

数字 \(X\)\([l,r]\) 最后一次出现位置的下标减去第一次出现位置的下标。

等价于:

数字 \(X\)\([l,r]\) 中每次出现的下标相邻的差的绝对值。

思路

一个朴素的想法:

  • 修改:用 \(set\) 动态维护维护每个位置的数与之相同的前面的数的位置,时间复杂度 \(O(\log N)\)
  • 查询暴力一个一个查 \(\sum_{i=l}^{i\le r} i-\texttt{pre}_i (\texttt{pre}_i \ge l)\) ,时间复杂度\(O(N)\)

考虑优化查询。

观察到每两个建立好的二元组 \((\texttt{pre}_i,i)\) 能对区间 \([l,r]\) 产生贡献当且仅当:

  • \(\texttt{pre}_i \ge l\)
  • \(i \le r\)
  • 以及操作的相对时间 \(T\)\(change\texttt T \le query\texttt T\)

联系到 \(\texttt{P4390}\) ,这不就相当于是看成一个二维数对,点\((x,y)\) 就是上述二元组,查询的便是一个 \((l,l)\)\((r,r)\) 构成的矩阵。

用cdq分治就可以解决了,cdq具体操作内容参考上题。(翻了翻 \(\texttt{CF}\) 上的记录或许还有其他做法,如莫队,扫描线...)。

注意修改操作一定要修改完整,当要删除一个二位数对的贡献时,加入与之相反贡献的相同数对即可。

代码

int a, b, s[N + 10], top, q[N + 10], ans[N + 10];
bool p[N + 10];
set<int> st[N + 10];
set<int>::iterator it;
struct node
{
    int opt, t, x, y, val;
    bool p;
} ask[N + 10];

inline bool cmp1(node &p1, node &p2)
{
    return p1.x < p2.x || p1.x == p2.x && p1.opt < p2.opt;
}

inline bool cmp2(node &p1, node &p2)
{
    return p1.t < p2.t;
}

inline void add(int n, int k)
{
    for (; n <= a; n += n & -n)
        q[n] += k;
}

inline int query(int n)
{
    if (!n)
        return 0;
    int res = 0;
    for (; n; n -= n & -n)
        res += q[n];
    return res;
}

inline void cdq(int l, int r)
{
    if (l == r)
        return;
    int mid = (l + r) >> 1;
    cdq(l, mid);
    cdq(mid + 1, r);
    for (int i = mid + 1; i <= r; i++)
        ask[i].p = 1;
    sort(ask + l, ask + r + 1, cmp2);
    for (int i = l; i <= r; i++)
    {
        if (!ask[i].p)
        {
            if (ask[i].opt == 1)
                add(ask[i].y, ask[i].val);
        }
        else
        {
            if (ask[i].opt == 2)
                ans[ask[i].t] += query(ask[i].y) * ask[i].val;
        }
    }
    for (int i = l; i <= r; i++)
    {
        if (!ask[i].p && ask[i].opt == 1)
            add(ask[i].y, -ask[i].val);
        ask[i].p = 0;
    }
}

signed main()
{
    // freopen("in1.in", "r", stdin);
    a = read();
    b = read();
    for (int i = 1; i <= a; i++)
    {
        s[i] = read();
        if (!st[s[i]].empty())
            ask[++top] = (node){1, 0, *st[s[i]].rbegin(), i, i - *st[s[i]].rbegin()};
        st[s[i]].insert(i);
    }
    int opt, x, y, z, w;
    for (int i = 1; i <= b; i++)
    {
        opt = read();
        x = read();
        y = read();
        if (opt == 1)
        {
            //delete
            z = w = 0;
            it = st[s[x]].find(x);
            if (it != st[s[x]].begin())
            {
                --it;
                ask[++top] = (node){1, i, *it, x, -(x - *it)};
                z = *it;
                ++it;
            }
            ++it;
            if (it != st[s[x]].end())
                ask[++top] = (node){1, i, x, *it, -(*it - x)}, w = *it;
            if (z && w)
                ask[++top] = (node){1, i, z, w, w - z};
            st[s[x]].erase(x);
            //insert
            z = w = 0;
            st[y].insert(x);
            it = st[y].find(x);
            if (it != st[y].begin())
            {
                --it;
                ask[++top] = (node){1, i, *it, x, x - *it};
                z = *it;
                ++it;
            }
            ++it;
            if (it != st[y].end())
                ask[++top] = (node){1, i, x, *it, *it - x}, w = *it;
            if (z && w)
                ask[++top] = (node){1, i, z, w, z - w};
            s[x] = y;
        }
        else
        {
            p[i] = 1;
            ask[++top] = (node){2, i, x - 1, x - 1, 1};
            ask[++top] = (node){2, i, x - 1, y, -1};
            ask[++top] = (node){2, i, y, x - 1, -1};
            ask[++top] = (node){2, i, y, y, 1};
        }
    }
    sort(ask + 1, ask + top + 1, cmp1);
    cdq(1, top);
    for (int i = 1; i <= b; i++)
        if (p[i])
            printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2021-03-01 19:44  RedreamMer  阅读(130)  评论(0编辑  收藏  举报