zoukankan      html  css  js  c++  java
  • 【树状数组 思维题】luoguP3616 富金森林公园

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

    题目描述

    博艾的富金森林公园里有一个长长的富金山脉,山脉是由一块块巨石并列构成的,编号从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
    ",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
    ",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 }
  • 相关阅读:
    测试开发进阶之路,2020 我们砥砺同行!
    【上海/北京/杭州】七牛云工程效率部直聘
    测试开发基础|一文搞定计算机网络(一)
    点点点工程师真的要被淘汰吗?
    电商性能测试实战 | JMeter 插件 Ultimate Thread Group 完成梯度递增场景的压测
    将H264与AAC打包Ipad可播放的TS流的总结
    Ubuntu阿里云搭建Mono.net环境
    利用正则表达式排除特定字符串
    你敢不敢不要让我这么忙
    离我而去的附录H
  • 原文地址:https://www.cnblogs.com/antiquality/p/8532922.html
Copyright © 2011-2022 走看看