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

题目空降

Description

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

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

Input

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

Output

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

Analysis

原题是查询旋转 45 的正方形内的点数。
这里写图片描述
我们将其旋转回来,即旋转 45
这里写图片描述
即化为一个三维偏序问题:
1. 时间
2. x + a x
3. x a x

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

P.S.

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

方法1 显然 更简单。

但是!!!

这里写图片描述
对于一个 S I Z E = 360000 的数组, O ( S I Z E × l o g 2 N ) 的复杂度显然是不可取的。
所以应取方法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;
}

猜你喜欢

转载自blog.csdn.net/ace_killing/article/details/79151443