树状数组、差分、前缀和、离散化

题目描述

博艾的富金森林公园里有一个长长的富金山脉,山脉是由一块块巨石并列构成的,编号从1到N。每一个巨石有一个海拔高度。而这个山脉又在一个盆地中,盆地里可能会积水,积水也有一个海拔高度,所有严格低于这个海拔高度的巨石,就会在水面下隐藏。

由于地壳运动,巨石的海拔高度可能会随时变化,每次一块的巨石会变成新的海拔高度。当然,水面的高度也会随时发生变化。

因为有这样奇妙的地质奇观,吸引了很多游客来游玩。uim作为一个游客,可以告诉你此时水位海拔,你得告诉他,能看到有几个连续露出水面的部分。(与水面持平我们也认为是露出)

输入输出格式

输入格式:

第一行两个整数N和M,分别表示N块石头,M个询问。

接下来一行,N个整数Ai表示每个巨石的初始海拔。

接下来M行,每行有两个或者三个数,每一行如果第一个数是1,那么后面跟一个Bj,表示水面海拔。如果第一个数是2,后面跟两个整数,Cj和Dj,表示编号Cj的巨石海拔变为Dj。

输出格式:

对于每个"1"询问,给出一个整数答案,也就是露出了几部分的山峰。

说明

10%的数据, N,M<=2000

另外30%的数据, 只有"1"的询问。

100%的数据, 1<=N,M<=200000,1<=Ai,Bj,Dj<=10^9,一定有"1"询问


题目分析

首先考虑暴力。每一次直接修改,查询时候$O(n)$查询,总复杂度$O(NM)$这样能拿50分。比赛做的时候有想过用树状数组维护,但是由于每次高度查询都不相同,因此没有搞出来。

后来去请教YKH,也看了看他的代码。这题用树状数组维护前缀和没有错,不过还要涉及到差分和离散化的应用。

容易发现在高度已知的情况下,对于相邻两个元素$f[i] > f[i-1]$,当且仅当$f[i-1] < query-height < f[i]$时对答案有贡献。那么可以看出,对于一组$f[i-1],f[i]$,它对任意$f[i-1] < height < f[i]$都有价值为1的贡献。显而易见的,这个特性使得我们可以用线性数据结构维护它。而差分+单点查询区间修改的树状数组是不错的选择(当然线段树也可以并且思维复杂度略小,不过比有差分的树状数组要慢不少)。

我们先将操作行为全部读入,考虑离线做法。

看到数据范围就知道直接用下标作为高度是会原地爆炸的。所以先将原始点和操作点都先存储起来,再对它们一起离散化。这样就可以避免已经离散化的一个数列例如{$1,3,5,7(原始)$}→{$1,2,3,4(离散化)$}再修改出未曾安排过离散的数例如{$1,3,5,7(原始)$}→{$1,3,5,4(修改后)$}→{$1,2,3,?(此时离散化爆炸)$}。

所有数据都读入之后,再对所有点按高度排序(不管它本身的属性是询问点还是修改点)。接下去先对点集手工离散化(不用unique的原因是这里不同类型的点离散化操作不同)。

离散化后我们拥有的数据应该是这样的:

$Q[i][0..2] i≤m$  记录所有操作

$按照高度排序的s[]$  记录所有点

数据既然已经预处理好了,接下去就是先对$tot$个点处理一遍了。处理也就不过是按照差分的思路单点修改。

处理操作时,对于query我们直接用树状数组对于查询点离散后的高度求前缀和;对于update我们要先考虑修改前的点是否与相邻的点产生贡献。因为这里的历史性是连续的(当前状况只从上一次操作转移过来),所以差分可以轻松完成撤销操作——只要根据原数据的逆操作即可。同理,更新过后再在左右检查一遍是否对答案有贡献,这样复杂度就是有保证的了。实测此方法540ms可过。

上YKH的代码(注释是我学习他代码过程中边看边打的)

 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4
 5 int read()
 6 {
 7     char c;while(c=getchar(),c<'0'||c>'9');
 8     int x=c-'0';while(c=getchar(),c>='0'&&c<='9')x=x*10+c-'0';
 9     return x;
10 }
11
12 int N,M,tot,x,y,Q[200005][3],H[200005];
13
14 struct wx{
15     int x,y,c;//c-0原先高度 c-1询问 c-2修改操作
16 }S[400005];
17
18 int cmp(wx x,wx y){return x.x<y.x;}
19
20 int ar[400005];
21
22 void add(int x,int y)
23 {
24     for(int i=x;i<=tot;i+=i&-i){
25         ar[i]+=y;
26     }
27     return ;
28 }
29
30 int get(int x)
31 {
32     int tot=0;
33     for(int i=x;i>0;i-=i&-i){
34         tot+=ar[i];
35     }
36     return tot;
37 }
38
39 int main()
40 {
41 //    freopen("x.txt","r",stdin);
42 //    freopen("w.txt","w",stdout);
43     N=read(),M=read();
44     register int i,j;
45         for(i=1;i<=N;i++)x=read(),S[++tot]=(wx){x,i,0};
46         for(i=1;i<=M;i++){
47             Q[i][0]=read(),y=read();
48             if(Q[i][0]==2){
49                 Q[i][1]=y;y=read();
50                 S[++tot]=(wx){y,i,2};
51                 continue;
52             }
53             S[++tot]=(wx){y,i,1};
54         }
55
56     //Q[i][0]表示操作的类型
57
58     sort(S+1,S+tot+1,cmp);
59     S[0].x=-2e9;
60     int col=0;
61         for(i=1;i<=tot;i++){
62             if(S[i].x!=S[i-1].x)col++;
63             if(S[i].c==1)Q[S[i].y][1]=col;
64             else if(S[i].c==2)Q[S[i].y][2]=col;
65             else H[S[i].y]=col;//对H[]离散化
66         }
67         for(i=1;i<=N;i++){
68             if(H[i]>H[i-1]){
69                 add(H[i-1]+1,1);
70                 add(H[i]+1,-1);
71             }
72         }
73         for(i=1;i<=M;i++){
74             if(Q[i][0]==1)printf("%d\n",get(Q[i][1]));
75             else {//离线update
76                 if(H[Q[i][1]]>H[Q[i][1]-1]){
77                     add(H[Q[i][1]-1]+1,-1);
78                     add(H[Q[i][1]]+1,1);
79                 }
80                 if(Q[i][1]<N && H[Q[i][1]]<H[Q[i][1]+1]){
81                     add(H[Q[i][1]]+1,-1);
82                     add(H[Q[i][1]+1]+1,1);
83                 }
84                 H[Q[i][1]]=Q[i][2];
85                 if(H[Q[i][1]]>H[Q[i][1]-1]){
86                     add(H[Q[i][1]-1]+1,1);
87                     add(H[Q[i][1]]+1,-1);
88                 }
89                 if(Q[i][1]<N && H[Q[i][1]]<H[Q[i][1]+1]){
90                     add(H[Q[i][1]]+1,1);
91                     add(H[Q[i][1]+1]+1,-1);
92                 }
93             }
94         }
95     return 0;
96 }

这是我的代码(自认为可读性挺高emm)

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N = 2*1e5+35;
 4 const int M = 2*1e5+35;
 5 struct point
 6 {
 7     int x,y,c;
 8 }s[N+M];
 9 int Q[M][3],height[2*N],dl[2*N],n,m,tot;
10 inline int lowbit(int x){return x&-x;}
11 inline void add(int x, int c){for (; x<=tot; x+=lowbit(x))dl[x]+=c;}
12 inline int query(int x)
13 {
14     int ret = 0;
15     for (; x; x-=lowbit(x))ret+=dl[x];
16     return ret;
17 }
18 inline int read()
19 {
20     int x = 0;char c = getchar();
21     while (c<'0'||c>'9')c = getchar();
22     while (c>='0'&&c<='9'){x = x*10+c-48;c = getchar();}
23     return x;
24 }
25 bool cmp(point a, point b){return a.x<b.x;}
26 int main()
27 {
28     n = read();m = read();
29     for (int i=1; i<=n; i++)s[++tot] = (point){read(), i, 0};
30     for (int i=1; i<=m; i++)
31     {
32         Q[i][0] = read();
33         int y = read();
34         if (Q[i][0]-1){
35             Q[i][1] = y;y = read();
36             s[++tot] = (point){y, i, 2};
37             continue;
38         }
39         s[++tot] = (point){y, i, 1};
40     }
41     sort(s+1, s+tot+1, cmp);
42     int col = 0;
43     s[0].x = -2*1e9;
44     for (int i=1; i<=tot; i++)
45     {
46         if (s[i].x!=s[i-1].x)col++;
47         if (s[i].c==2)Q[s[i].y][2] = col;
48         else if (s[i].c==1)Q[s[i].y][1] = col;
49         else height[s[i].y] = col;
50     }
51     for (int i=1; i<=n; i++)
52     {
53         if (height[i]>height[i-1])
54         {
55             add(height[i]+1, -1);
56             add(height[i-1]+1, 1);
57         }
58     }
59     for (int i=1; i<=m; i++)
60     {
61         if (2-Q[i][0]){printf("%d\n",query(Q[i][1]));continue;}
62         int qs = Q[i][1];
63         if (height[qs-1]<height[qs])
64         {
65             add(height[qs]+1, 1);
66             add(height[qs-1]+1, -1);
67         }
68         if (qs < n&&height[qs]<height[qs+1])
69         {
70             add(height[qs+1]+1, 1);
71             add(height[qs]+1, -1);
72         }
73         height[qs] = Q[i][2];
74         if (height[qs-1]<height[qs])
75         {
76             add(height[qs]+1, -1);
77             add(height[qs-1]+1, 1);
78         }
79         if (qs < n&&height[qs]<height[qs+1])
80         {
81             add(height[qs+1]+1, -1);
82             add(height[qs]+1, 1);
83         }
84     }
85     return 0;
86 }

转载于:https://www.cnblogs.com/antiquality/p/8532922.html

【树状数组 思维题】luoguP3616 富金森林公园相关推荐

  1. hdu1166敌兵布阵 树状数组裸题

    树状数组裸题 动态更新区间内的点,动态查询区间和 敌兵布阵 ac代码 #include<iostream> #include<algorithm> #include<cs ...

  2. 树状数组板子题之一:hdu 1166 敌兵布阵

    树状数组板子题之一:hdu 1166 敌兵布阵 题目链接:hdu 1166 敌兵布阵 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手 ...

  3. HDU1166 敌兵布阵(树状数组模板题)

    敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...

  4. hdu 1166 敌兵布阵 树状数组 模板题

    这题是树状数组入门的一模板题,非常基础,被小白成为"赤裸裸"的入门题,哈哈,一个plus,一个sum全部搞定 #include<stdio.h> #include< ...

  5. acwing-241. 楼兰图腾-树状数组板子题+开脑洞

    原题链接 题意:给你一组数,让你找出所有aiak的个数与ai>aj<ak的个数. 思路: 看到这个题瞬间想到了三层的暴力(暴力破万物),但是n是200000,跑三层服务器会跑吐的,必然超时 ...

  6. hdoj 4417 Super Mario 【树状数组 + 思维】

    Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  7. HDU - 5775 - Bubble Sort( 树状数组 + 思维 )

    题目链接:点击进入 题目 题意 问在给出的冒泡排序过程中,一个数到达的最右边位置与最左边位置距离差. 思路 对于一个数,位置 i ,假设右边比它小的数有 r 个,左边比它大的数有 l 个,最右边到达的 ...

  8. C++树状数组模板题 敌兵布阵解题报告

    题目描述: C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活 ...

  9. NOIP刷题记录(打鼹鼠)——二维树状数组板子题

    一.题目描述 在这个"打鼹鼠"的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大--).洞口都在一个大小为n的正方形中.这个正方形在一个平面直角坐标系中,左下角为 ...

最新文章

  1. 第十六届全国大学生智能车比赛掠影
  2. P2737 [USACO4.1]麦香牛块Beef McNuggets 数学题 + 放缩思想
  3. C++11如何减少内存拷贝次数
  4. boost::local_function模块实现不可复制的 local_function的测试程序
  5. C++11新特性学习
  6. linux命令看文件内容,Linux文件内容查看相关命令
  7. MTCNN-tensorflow源码解析-gen_landmark_aug_12.py;gen_imglist_pnet.py
  8. Android 8.0 学习(12)---init.rc语法及解析过程总结
  9. php获取ajax data,HTML 获取 PHP 接口数据(ajax)
  10. 计算机组成原理—cpu于主存的连接(例题)
  11. Windows 10下使用Xshell5连接虚拟机的ubuntu18系统
  12. 1 Kubernetes快速入门
  13. VMWare Workstation 使用UEFI启动
  14. java编程中常见的拼写错误
  15. Qua Vadis Eclipse? 第一部分
  16. 物理内存管理-ucore操作系统的PADDR宏
  17. linux的版本(部分转载)
  18. uniapp生成随机数字
  19. VMware认证专家(VCP)详细介绍
  20. 解决CentOS删除文件后没有释放磁盘空间(lsof命令)

热门文章

  1. 【数论】蓝桥20:数列求值
  2. 【动态规划】P1048 01背包问题:采药
  3. ITU-R BT.1788建议书 对多媒体应用中视频质量的主观评估方法
  4. sqlserver存储过程加锁后怎么解锁_MySQL 的加锁处理,你都了解的一清二楚了吗?...
  5. linux根据端口号找目录,linux篇---根据端口号查看进程位置
  6. linux机器的物理内存监控,Linux内存监控工具
  7. Ubuntu 18.04 软件源修改成国内源
  8. Java使用IntelliJ IDEA配置Maven并管理一个webapp项目
  9. 华军java_Java SE Runtime Environment 8
  10. pkill mysql_centos下kill、killall、pkill命令区别