[JZOJ3615]【NOI2014模拟】数列(平面几何+二维线段树)

Description

  给定一个长度为n的正整数数列a[i]。

  定义2个位置的f值为两者位置差与数值差的和,即f(x,y)=|x-y|+|a[x]-a[y]|。

  你需要写一个程序支持2种操作(k都是正整数):

  Modify x k:将第x个数的值修改为k。

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

Main

  令F(x,y)=|x-y|+|a[x]-a[y]|,每次可以将a[x]修改为k,或者查询满足f(x,i)≤k的个数。

Analysis

【二维线段树/树状数组】  

  看到题目这样的粗俗,笼统,简洁明了,便知道一定要用一个数据结构维护

  我们可以将x抽象为x坐标,a[x]抽象为y坐标,那么f(x,y)的意思就显然了:表示x点(x,a[x])和y点(y,a[y])的曼哈顿距离。

  但是这个曼哈顿距离比较蛋疼,不着急,画(截)个图看看。

  

  我们发现图片大概是这样的,发现这个可以取的范围是个菱形,旋转90°就变成了正方形。旋转就是使坐标(x,y)变成(x+y,x-y)

  

  那么,问题就转化为每次可以加入一些点,求某个正方形内包含点的个数。

【K-Dtree】

  。。。

【cbq分治+主席树】

  ...

Solution

  我们考虑用一个数据结构来维护,如二维线段树/树状数组。

  每次相当于插入点(x+a[x],x-a[x])到图中,线段树/树状数组维护左下角为(x1,y1),右上角为(x2,y2)的矩阵信息,查询即可。

  对于二维线段树,有两种实现方法。一种是划分为4个区域,第二种是划分为2个,根据长宽的大小来切。

  

  要动态开点。直接做会超时,要加优化:当前子树没有贡献,直接退出。

【K-Dtree】

  。。。

【cbq分治+主席树】

  ...

Code

{$inline on}
var
        ch,lala:char;
        n,m,i,x,y,tot,ans:longint;
        a:array[0..60000] of longint;
        tree:array[0..2500000] of longint;
        son:array[0..2500000,1..2] of longint;
procedure dispose; inline;
begin
        read(ch);

        lala:=‘1‘;
        while lala<>‘ ‘ do read(lala);

        readln(x,y);
end;

procedure fyj(x:longint); inline;
begin
        if son[x,1]=0 then
        begin
                inc(tot); son[x,1]:=tot;
                inc(tot); son[x,2]:=tot;
        end;
end;

procedure change(root,x1,y1,x2,y2,fx,fy:longint);  inline;
var
        mid:longint;
begin
        if (x1=x2) and (y1=y2) and (x1=fx) and (y1=fy) then
        begin
                inc(tree[root]);

                exit;
        end;

        fyj(root);

        if x2-x1>=y2-y1 then
        begin
                mid:=(x1+x2) shr 1;

                if fx<=mid then
                        change(son[root,1],x1,y1,mid,y2,fx,fy)
                else
                        change(son[root,2],mid+1,y1,x2,y2,fx,fy);
        end
        else
        begin
                mid:=(y1+y2) shr 1;

                if fy<=mid then
                        change(son[root,1],x1,y1,x2,mid,fx,fy)
                else
                        change(son[root,2],x1,mid+1,x2,y2,fx,fy);
        end;

        tree[root]:=tree[son[root,1]]+tree[son[root,2]];
end;

procedure find(root,x1,y1,x2,y2,fx1,fy1,fx2,fy2:longint);  inline;
var
        mid:longint;
begin
        if (x1=fx1) and (x2=fx2) and (y1=fy1) and (y2=fy2) then
        begin
                ans:=ans+tree[root];

                exit;
        end;

        if son[root,1]=0 then
                exit;

        if x2-x1>=y2-y1 then
        begin
                mid:=(x1+x2) shr 1;

                if fx2<=mid then
                        find(son[root,1],x1,y1,mid,y2,fx1,fy1,fx2,fy2)
                else
                if fx1>mid then
                        find(son[root,2],mid+1,y1,x2,y2,fx1,fy1,fx2,fy2)
                else
                begin
                        find(son[root,1],x1,y1,mid,y2,fx1,fy1,mid,fy2);
                        find(son[root,2],mid+1,y1,x2,y2,mid+1,fy1,fx2,fy2);
                end;
        end
        else
        begin
                mid:=(y1+y2) shr 1;

                if fy2<=mid then
                        find(son[root,1],x1,y1,x2,mid,fx1,fy1,fx2,fy2)
                else
                if fy1>mid then
                        find(son[root,2],x1,mid+1,x2,y2,fx1,fy1,fx2,fy2)
                else
                begin
                        find(son[root,1],x1,y1,x2,mid,fx1,fy1,fx2,mid);
                        find(son[root,2],x1,mid+1,x2,y2,fx1,mid+1,fx2,fy2);
                end;
        end;
end;
begin
        readln(n,m);

        tot:=1;

        for i:=1 to n do
        begin
                read(a[i]);

                change(1,0,0,320000,280000,i+a[i]+100000,i-a[i]+100000);
        end;

        readln;

        for i:=1 to m do
        begin
                dispose;

                if ch=‘M‘ then
                begin
                        change(1,0,0,320000,280000,x+y+100000,x-y+100000);

                        a[x]:=y;
                end
                else
                begin
                        ans:=0;
                        find(1,0,0,320000,280000,x+a[x]-y+100000,x-a[x]-y+100000,x+a[x]+y+100000,x-a[x]+y+100000);

                        writeln(ans);
                end;
        end;
end.

原文地址:https://www.cnblogs.com/2016fyj/p/8337925.html

时间: 2024-08-23 13:27:18

[JZOJ3615]【NOI2014模拟】数列(平面几何+二维线段树)的相关文章

[CSP-S模拟测试]:表格(动态开点二维线段树+离散化)

题目传送门(内部题112) 输入格式 一个数$N$,表示矩形的个数. 接下来$N$行,每行四个整数$X_a,Y_a,X_b,Y_b$.分别表示每个矩形左下角和右上角的坐标. 保证$(X_a<X_b,Y_a<Y_b)$. 输出格式 一行,表示能看到的颜色数量. 样例 样例输入: 30 -1 1 12 1 3 5-4 0 5 4 样例输出: 4 数据范围与提示 样例解释: 数据范围: 对于$10\%$的数据,保证$N\leqslant 100,|X_a,X_b,Y_a,Y_b|\leqslant

poj 1195:Mobile phones(二维线段树,矩阵求和,经典题)

Mobile phones Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 14391   Accepted: 6685 Description Suppose that the fourth generation mobile phone base stations in the Tampere area operate as follows. The area is divided into squares. The

HDU1832 二维线段树求最值(模板)

Luck and Love Time Limit: 10000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 50 Accepted Submission(s): 20   Problem Description 世界上上最远的距离不是相隔天涯海角而是我在你面前可你却不知道我爱你                ―― 张小娴 前段日子,枫冰叶子给Wiskey做了个征婚启事,聘

ZOJ 2859 二维线段树

思路:自己写的第二发二维线段树1A,哈哈,看来对二维的push操作比较了解了:但是还没遇到在两个线段树中同时进行push操作的,其实这题我是想在x维和y维同时进行push操作的,但是想了好久不会,然后看到这题又给出10秒,然后想想在x维线段直接单点查询肯定也过了,然后在第二维就只有pushup操作,在第一维线段树没有pushup操作.要是在第一维也有pushup操作的话,那就不用单点查询那么慢了.不过也A了,想找题即在二维同时进行pushup和pushdown操作的. #include<iost

[POJ2155] Matrix(二维线段树,树套树)

题目链接:http://poj.org/problem?id=2155 题意:给一个01矩阵,两个操作,翻转:子矩阵里每一个数都由0变1,1变0. 查询:查询某一点是0还是1. 一直以为二维线段树就是开一个线段树数组的我- 这题暴力更新每一个小矩形,翻转就+1,最后看看某点的奇偶. 写屎了,特别注意的是在外层查询的时候要先把当前层的内层query掉,接着再向下扩展.这样外层就不用lazy啦 1 #include <algorithm> 2 #include <iostream> 3

poj1195 Mobile phones 二维线段树入门

二维线段树就是树套树,线段树套线段树... #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) #define lson l,m,rt<<1 #

UVA 11297 Census ——二维线段树

[题目分析] 二维线段树模板题目. 简直就是无比的暴力.时间复杂度为两个log. 标记的更新方式比较奇特,空间复杂度为N^2. 模板题目. [代码] #include <cstdio> #include <cstring> //#include <cmath> #include <cstdlib> #include <map> #include <set> #include <queue> #include <str

tyvj P1716 - 上帝造题的七分钟 二维树状数组区间查询及修改 二维线段树

P1716 - 上帝造题的七分钟 From Riatre    Normal (OI)总时限:50s    内存限制:128MB    代码长度限制:64KB 背景 Background 裸体就意味着身体. 描述 Description “第一分钟,X说,要有矩阵,于是便有了一个里面写满了0的n×m矩阵.第二分钟,L说,要能修改,于是便有了将左上角为(a,b),右下角为(c,d)的一个矩形区域内的全部数字加上一个值的操作.第三分钟,k说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作.第

POJ 2155 二维线段树

POJ 2155  二维线段树 思路:二维线段树就是每个节点套一棵线段树的树. 刚开始因为题目是求A[I,J],然后在y查询那直接ans^=Map[i][j]的时候没看懂,后面自己把图画出来了才理解. 因为只有0和1,所以可以用异或来搞,而不需要每次都需要修改. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #incl