POJ - 2528 区间离散化,线段树区间修改,区间询问

  这个题非常有意思的地方是,我们发现区间[1,4]和[5,8]是紧挨着的,因为这个的数代表的是一段区间,原本我们对于普通的离散,

 a[1]=1,a[2]=5,a[3]=6,a[4]=8;数组下标就是重新离散的位置,但是a[2]和a[3]明显不重叠,为此我们需要重新考虑离散的内容,其实不妨这样,如果区间的间隔大于1,那么我们插入一个数a[i]+1,这样就强行把a[i]和a[i+1]分开,因为

如三张海报为:1~10 1~4 6~10

离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
第一张海报时:墙的1~4被染为1;
第二张海报时:墙的1~2被染为2,3~4仍为1;
第三张海报时:墙的3~4被染为3,1~2仍为2。
最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。

新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)

为什么会这样呢?我们这样考虑,如果a[1]=3,a[2]=4那么他们两个是相邻的,这样其实离散后他们还是相邻的(因为1,2在题目的意义是相邻的),但是如果是a[1]=3,a[2]=5哈希后其实是(1,2) 它是相邻的(实际上3,5不相邻),于是我们想到这样,既然我们中间有一段(没有数那一段)是我们所忽略的,我们可以新加一个数4,如

a[1]=3    a[2]=4   a[3]=5; 这样哈希后是1,2,3,我们认为3->1而5->3,(1,3)其实是不相邻的。这道题也就没什么问题了,最后区间查询+区间修改。

 

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 10005;
int sum[maxn<<4];
int vis[maxn<<3];
int li[maxn*2];
int ri[maxn*2];
int lsh[maxn<<2];
void pushdown(int root){//把节点信息传给儿子节点
   sum[root<<1]=sum[root];//相应这个节点如果最后被修改成这个结果,那么他的儿子节点也应该修改
   sum[root<<1|1]=sum[root];
   sum[root]=-1;//清空laze标记
}
int ans;
void update(int root,int L,int R,int C,int l,int r){
   if (L<=l && r<=R)//如果被修改区间完全盖过当前区间
   {
       sum[root]=C;//更新
       return;
   }
   if (sum[root]!=-1)
    pushdown(root);//如果不满足上述条件,我们需要把节点的信息更新,
   int m=(l+r)>>1;
   if (m>=R)update(root<<1,L,R,C,l,m);//信息完全在左子树
   else if(L>m)update(root<<1|1,L,R,C,m+1,r);//完全在右子树
   else update(root<<1,L,m,C,l,m),update(root<<1|1,m+1,R,C,m+1,r);
}
void query(int root,int l,int r){
//     cout<<root<<endl;
     if (sum[root]!=-1 && !vis[sum[root]])//如果当前节点的信息已经能包含所有的节点信息,并且这个节点的信息是第一次访问到
     {
         ans++;
         vis[sum[root]]=1;
         return;
     }
     if (l==r)
     {
         return;
     }
     if (sum[root]!=-1)
        pushdown(root);//更新标记
     int m=(l+r)/2;
     query(root<<1,l,m);
     query(root<<1|1,m+1,r);
}
int main(){
  int t;
  int n;
  scanf("%d",&t);
  while(t--){
    scanf("%d",&n);
    memset(sum,-1,sizeof(sum));
    memset(vis,0,sizeof(vis));
    int tot=0;
    for (int i=0;i<n;i++)
    {
        scanf("%d%d",&li[i],&ri[i]);
        lsh[tot++]=li[i];
        lsh[tot++]=ri[i];
    }
    sort(lsh,lsh+tot);
    int mm=unique(lsh,lsh+tot)-lsh;
    int tt=mm;
    for (int i=1;i<tt;i++)
    {
        if (lsh[i]-lsh[i-1]>1)
            lsh[mm++]=lsh[i-1]+1;
    }
    sort(lsh,lsh+mm);//排序 按照数组下标进行离散
    for (int i=0;i<n;i++){
        int x=lower_bound(lsh,lsh+mm,li[i])-lsh;//查找左边的离散后的号码
        int y=lower_bound(lsh,lsh+mm,ri[i])-lsh;//查找右边的离散后的号码
        update(1,x,y,i,0,mm-1);//更新
    }
    ans=0;
    query(1,0,mm-1);
    printf("%d\n",ans);
  }
  return 0;
}

 

全部评论

相关推荐

上周组里招人,我面了六个候选人,回来跟同事吃饭的时候聊起一个让我挺感慨的现象。前三个候选人,算法题写得都不错。第一道二分查找,五分钟之内给出解法,边界条件也处理得干净。第二道动态规划,状态转移方程写对了,空间复杂度也优化了一版。我翻他们的简历,力扣刷题量都在300以上。后三个呢,就有点参差不齐了。有的边界条件没处理好,有的直接说这道题没刷过能不能换个思路讲讲。其中有一个女生,我印象特别深——她拿到题之后没有马上写,而是先问我:“面试官,我能先跟你确认一下我对题目的理解吗?”然后她把自己的思路讲了一遍,虽然最后代码写得不是最优解,但整个沟通过程非常顺畅。这个女生的代码不是最优的,但当我问她“如果这里是线上环境,你会怎么设计’的时候,她给我讲了一套完整的方案——异常怎么处理、日志怎么打、怎么平滑发布。她对这是之前在实习的时候踩过的坑。”我在想LeetCode到底在筛选什么?我自己的经历可能有点代表性。我当年校招的时候,也是刷了三百多道题才敢去面试。那时候大家都刷,你不刷就过不了笔试关。后来工作了,前三年基本没再打开过力扣。真正干活的时候,没人让你写反转链表,也没人让你手撕红黑树。更多的是:这个接口为什么慢了、那个服务为什么OOM了、线上数据对不上了得排查一下。所以后来我当面试官,慢慢调整了自己的评判标准。算法题我还会出,但目的变了。我出算法题,不是想看你能不能背出最优解。而是想看你拿到一个陌生问题的时候,是怎么思考的。你会先理清题意吗?你会主动问边界条件吗?你想不出来的时候会怎么办?你写出来的代码,变量命名乱不乱、结构清不清楚?这些才是工作中真正用得到的能力。LeetCode是一个工具,不是目的。它帮你熟悉数据结构和常见算法思路,这没问题。但如果你刷了三百道题,却说不清楚自己的项目解决了什么问题、遇到了什么困难、你是怎么解决的,那这三百道题可能真的白刷了。所以还要不要刷LeetCode?要刷,但别只刷题。刷题的时候,多问自己几个为什么:为什么用这个数据结构?为什么这个解法比那个好?如果换个条件,解法还成立吗?把刷题当成锻炼思维的方式,而不是背答案的任务。毕竟面试官想看到的,从来不是一台背题机器,而是一个能解决问题的人。
牛客51274894...:意思是光刷力扣还不够卷
AI时代还有必要刷lee...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务