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

题目空降

Description

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

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

Input

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

Output

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

Analysis

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

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

P.S.

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

方法1 显然 更简单。

但是!!!

这里写图片描述
对于一个SIZE=360000的数组, O(SIZE×log2N) 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值