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}\leq k\)。

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

Input

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

Output

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

Analysis

原题是查询旋转\(45^\circ\)的正方形内的点数。

我们将其旋转回来,即旋转\(45^\circ\)。

即化为一个三维偏序问题:

  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 显然 更简单。

但是!!!


对于一个\(SIZE=360000\)的数组,\(O\left(SIZE\times log_2N\right)\) 的复杂度显然是不可取的。
所以应取方法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;
}

原文地址:https://www.cnblogs.com/Ace-Killing/p/JAN24_A_T3.html

时间: 2024-07-31 18:11:24

January 24th 模拟赛A T3【NOI2014模拟】数列 Solution的相关文章

2017.5.27 NOIP模拟赛(hzwer2014-5-16 NOIP模拟赛)

期望得分:100+100+60+30=290 实际得分:100+20+60+0=180 当务之急:提高一次正确率 Problem 1 双色球(ball.cpp/c/pas) [题目描述] 机房来了新一届的学弟学妹,邪恶的chenzeyu97发现一位学弟与他同名,于是他当起了善良的学长233 “来来来,学弟,我考你道水题检验一下你的水平……” 一个栈内初始有n个红色和蓝色的小球,请你按照以下规则进行操作 只要栈顶的小球是红色的,将其取出,直到栈顶的球是蓝色 然后将栈顶的蓝球变成红色 最后放入若干个

20180520模拟赛T3——chess

[问题描述] 小美很喜欢下象棋. 而且她特别喜欢象棋中的马. 她觉得马的跳跃方式很独特.(以日字格的方式跳跃) 小芳给了小美一张很大的棋盘,这个棋盘是一个无穷的笛卡尔坐标. 一开始\(time=0\)的时候,马在原点.每个时刻马都跳一步. 可是这个坐标图有点残缺,有几个点是不能跳到的. 然后小美很好奇在\(time=[0,K]\)中,马能跳到多少个不同的格子. [输入格式] 从文件chess.in中读入数据. 第一行两个数K,n表示时间上限和残缺的点的数量. 接下来n行,每行一个坐标 xi,yi

体育成绩统计——20180801模拟赛T3

体育成绩统计 / Score 题目描述 正所谓“无体育,不清华”.为了更好地督促同学们进行体育锻炼,更加科学地对同学们进行评价,五道口体校的老师们在体育成绩的考核上可谓是煞费苦心.然而每到学期期末时,面对海量的原始数据,如何对数据进行处理,得到同学们的体育总评成绩却又成了体育部老师的一大难题. 对于大一的同学们来说,体育课的总评成绩由五部分组成:体育课专项成绩(满分50分).长跑测试成绩(满分20分).“阳光长跑”成绩(满分10分).体质测试成绩(满分10分).“大一专项计划”成绩(满分10分)

@省选模拟赛03/16 - T3@ 超级树

目录 @description@ @solution@ @accepted code@ @details@ @description@ 一棵 k-超级树(k-SuperTree) 可按如下方法得到:取一棵深度为 k 的满二叉树,对每个节点向它的所有祖先连边(如果这条边不存在的话). 例如,下面是一个 4-超级树: 请统计一棵 k-超级树 中有多少条不同的简单有向路径,对 mod 取模. input 一行两整数 k, mod. output 一行一整数表示答案. example input1: 2

9.14 模拟赛

模拟赛第三弹~ T1 题意:给你一个数列,要求删掉任意一种整数,使得剩下的新数列中连续的相等的数最多 例如 2 7 3 7 7 3 3 7 7 5 7,删掉3以后剩的7有四个连续的,最多 思路:暴力枚举去掉哪个......这算是一道水题吧 代码丢了...... TAT T2 题意:有n本书,每本书有宽度和高度.现在你有无数个书架,每个书架的宽度为w,高度由最高的书决定 问在书本按顺序放的情况下,总的书架高度最小是多少 思路:dp,dp[i]表示做到第i本书时的最小高度和. 每次先找到能以编号j的

20161027模拟赛解题报告

20161027模拟赛解题报告 By shenben T1 数学题 模拟即可. 注意开long long T2 技巧题 图片为本题第一张图.(无奈,图传不上来) 首先第一问图中的“Y 字形”的数量,这么简单,在此不细讲. 详见代码 O(n)累加一下就好了 主要说说第二问怎么搞 预处理 每个点分别与其他那些点相连 权值为第1,2,3大(若没有2,3大,就忽略).记录一下权值与对应的点的标号.目的是方便下面的判断. 枚举入度>=3的点,即点B(有多个) 再枚举点B相连的D点(不是点A,C). Ste

[GRYZ]寒假模拟赛

写在前面 这是首次广饶一中的OIERS自编自导,自出自做(zuo)的模拟赛. 鉴于水平气压比较低,机(wei)智(suo)的WMY/XYD/HYXZC就上网FQ下海找了不少水(fei)题,经过他们优(le)美(se)的文字加工后,有故事有题目有人物有奸情的模拟赛正式呈上. 我是正文 题目名 GRYZ娱乐时刻 GRYZ追击时刻 GRYZ就餐时刻 源文件 hyxzc.cpp/c/pas clikar.cpp/c/pas eat.cpp/c/pas 输入文件 hyxzc.in clikar.in ea

[铁一中OI模拟赛]2017.8.19 Day1

T1 小Z的情书 题目链接 思考: 题目主要难度在于旋转后的位置如何,在手写了样例之后不难发现规律. #include <cstdio> #include <cstring> #define up(a,b,c) for(register int c=a;c<=b;++c) #define down(a,b,c) for(register int c=a;c>=b;--c) const int Maxn=1005; int n; bool Map[Maxn][Maxn],

8月15日模拟赛小结

今天的模拟赛是三道陈老师的题. 在 10:48 左右已经都能拿到了. 然后我觉得 T3 的蒙特卡洛算法不太稳, 于是调大参数. 本地不开 O2 需要 5s 左右, 开 O2 测需要 2s , 而时限是 3s . 我想着 XSY 也开了 O2 , 所以稳了. 结果比赛结束 T3 全部 TLE ..... 中途还犯了一次A题代码交到B题的错误. ZJT大爷说我从第 2 调到了倒数第 1 , QAQ . 得出结论: ① 很多评测机可能很辣鸡. 所以如果对于随机化算法之类的, 能开大迭代次数的标准是: