欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

January 24th 模拟赛A T3【NOI2014模拟】数列 Solution

程序员文章站 2022-06-22 21:08:07
...

题目空降

Description

给定一个长度为n的正整数数列ai
定义两个位置的f为两者位置差与数值差的和,即fx,y=|xy|+|axay|
你需要写一个程序支持两种操作(k都是正整数):
1. Modify x k:将第x个数的值修改为k
2. Query x k:询问有几个i满足fx,ik

询问不仅要考虑当前数列,还要考虑任意历史版本,即统计任意位置上出现过的任意数值与当前的axfk的对数。(某位置多次修改为同样的数值,按多次统计)

Input

1行两个整数n,q。分别表示数列长度和操作数。
2n个正整数,代表初始数列。
3q+2行每行一个操作。

Output

对于每次询问操作,输出一个非负整数表示答案。

Analysis

原题是查询旋转45的正方形内的点数。
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
我们将其旋转回来,即旋转45
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
即化为一个三维偏序问题:
1. 时间
2. x+ax
3. xax

经典的做法是CDQ分治。(用二维线段树/树状数组勿喷)(不会的请自行百度)
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
对于一段已时间为序的区间,将左段的修改点,右段的查询点提出。
用左段的修改点更新右段的查询点的答案。
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
对于这样的查询,(众所周知)暴力做法是不可取的。
我们考虑树状数组。
利用树状数组维护上下区间(h1,h2)之间的点。
稍加思考,便知道这样是不可取的。
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
因为这样红色区域的点会被出入树状数组多次。
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
所以我们考虑避免这样的问题。
一个可行的办法是将正方形左右界提取:l1,r
按照横坐标排序。
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
利用容斥原理,求出点数。(蓝色部分黄色部分)
至此,问题得解。

P.S.

瞎BB。
在重置树状数组时,有如下办法:
1. memset(f,0,sizeof(f));
2. 一个一个重新清

方法1 显然 更简单。

但是!!!

January 24th 模拟赛A T3【NOI2014模拟】数列 Solution
对于一个SIZE=360000的数组,O(SIZE×log2N) 的复杂度显然是不可取的。
所以应取方法2。

Code

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;

struct node
{
    int x, y, k, ans, pos;
    bool bj;
};

const int MaxAi = 360000, AbsValue = 180000;

int n, q;
node d[120005];
int final[60005];
node E[120005], Q[120005];
int Etot, Qtot;
int f[360005];

int Lowbit(int x)
{
    return x & (-x);
}

void Change(int cod, int value)
{
    while (cod <= MaxAi)
    {
        f[cod] += value;
        cod += Lowbit(cod);
    }
}

int Sum(int x)
{
    int rs = 0;

    while (x > 0)
    {
        rs += f[x];
        x -= Lowbit(x);
    }

    return rs;
}

int Query(int l, int r)
{
    return Sum(r) - Sum(l - 1);
}

bool cmp(node a, node b)
{
    return a.x < b.x;
}

bool Get()
{
    int c = 0;
    bool rtn;

    for (c = getchar(); (c == ' ') || (c == '\r') || (c == '\n'); c = getchar());

    rtn = c == 'Q';

    for (c = getchar(); (c != ' ') && (c != '\r') && (c != '\n'); c = getchar());

    return rtn;
}

void Dfs(int l, int r)
{
    if (l >= r)
        return;

    int mid = (l + r) / 2;
    Dfs(l, mid), Dfs(mid + 1, r);
    Etot = Qtot = 0;

    for (int i = l; i <= mid; i++)
        if (!d[i].bj)
        {
            E[++Etot] = d[i];
            E[Etot].pos = i;
        }

    for (int i = mid + 1; i <= r; i++)
        if (d[i].bj)
        {
            Q[++Qtot] = d[i];
            Q[Qtot].x -= Q[Qtot].k * 2 + 1;
            Q[Qtot].pos = i;
            Q[Qtot].bj = true;
            Q[++Qtot] = d[i];
            Q[Qtot].pos = i;
            Q[Qtot].bj = false;
        }

    sort(E + 1, E + Etot + 1, cmp);
    sort(Q + 1, Q + Qtot + 1, cmp);
    //memset(f, 0, sizeof(f)); 方法1

    for (int i = 1, j = 1; i <= Qtot; i++)
    {
        while ((j <= Etot) && (E[j].x <= Q[i].x))
            Change(E[j].y + AbsValue, 1), j++;

        if (Q[i].bj)
            d[Q[i].pos].ans -= Query(Q[i].y - Q[i].k * 2 + AbsValue, Q[i].y + AbsValue);
        else
            d[Q[i].pos].ans += Query(Q[i].y - Q[i].k * 2 + AbsValue, Q[i].y + AbsValue);
    }
    //方法2
    for (int i = 1, j = 1; i <= Qtot; i++)
    {
        while ((j <= Etot) && (E[j].x <= Q[i].x))
            Change(E[j].y + AbsValue, -1), j++;
    }
}

int main(int argc, char const *argv[])
{
    freopen("init.in", "r", stdin);
    scanf("%d%d", &n, &q);

    for (int i = 1; i <= n; i++)
    {
        int tmp;
        scanf("%d", &tmp);
        d[i].x = i + tmp;
        d[i].y = i - tmp;
        final[i] = i;
    }

    for (int i = 1; i <= q; i++)
    {
        d[n + i].bj = Get();

        if (!d[n + i].bj)
        {
            scanf("%d%d", &d[n + i].x, &d[n + i].y);
            final[d[n + i].x] = n + i;
            d[n + i].x += d[n + i].y;
            d[n + i].y = d[n + i].x - d[n + i].y * 2;
        }
        else
        {
            int tmp;
            scanf("%d%d", &tmp, &d[n + i].k);
            d[n + i].x = d[final[tmp]].x + d[n + i].k;
            d[n + i].y = d[final[tmp]].y + d[n + i].k;
        }
    }

    n += q;
    Dfs(1, n);

    for (int i = 1; i <= n; i++)
        if (d[i].bj)
            printf("%d\n", d[i].ans);

    return 0;
}
相关标签: JZOJ GDKOI模拟赛