分块

分块

当维护的信息不满足区间可加,可减性的时候,用线段树或者树状数组维护的时候较为困难,通过分块划分+预处理可以有效的解决一些问题

1 区间修改,区间查询

A Simple Problem with Integers POJ - 3468
将1… n 分成 n \sqrt{n} n 块,每块大小 n \sqrt{n} n ,对于每次修改或者查询,整块通过sum 直接加上,补足一块的暴力求解

const int maxn = 100010;
LL a[maxn],add[maxn],sum[maxn];
int pos[maxn],R[maxn],L[maxn];
int n,m,t;
void change(int l,int r,LL d){
  int p = pos[l],q = pos[r];
  if(p == q){
    for(int i = l;i <= r; ++i) a[i] += d;
    sum[p] += (r-l+1)*d;
  }
  else{
    for(int i = p+1;i <= q-1; ++i) add[i] += d;
    for(int i = l;i <= R[p];++i)
      a[i] += d;
    sum[p] += (R[p]-l+1)*d;
    for(int i = L[q];i <= r; ++i)
      a[i] += d;
    sum[q] += (r-L[q]+1)*d;
  }
}
LL ask(int l,int r){
  LL ans = 0;
  int p = pos[l],q = pos[r];
  if(p == q){
    for(int i = l;i <= r; ++i)
      ans += a[i];
    ans += (r-l+1)*add[p];
  }
  else{
    for(int i = p+1;i <= q-1; ++i)
      ans += sum[i]+add[i]*(R[i]-L[i]+1);
    for(int i = l;i <= R[p]; ++i)
      ans += a[i];
    ans += add[p]*(R[p]-l+1);
    for(int i = L[q];i <= r; ++i)
      ans += a[i];
    ans += add[q]*(r-L[q]+1);
  }
  return ans;
}
int main(void){

  cin>>n>>m;
  for(int i = 1;i <= n; ++i) scanf("%lld",&a[i]);
  LL t = sqrt(n);
  for(int i = 1;i <= t; ++i){
    L[i] = (i-1)*sqrt(n)+1;
    R[i] = i*sqrt(n);
  }
  if(R[t]  < n) t++,L[t] = R[t-1]+1,R[t] = n;
  // cout<<t<<endl;
  for(int i = 1;i <= t; ++i){
    for(int j = L[i];j <= R[i]; ++j){
      pos[j] = i;
      sum[i] += a[j];
    }
  }
  while(m--){
    char op[3];
    int l,r,x;
    scanf("%s%d%d",op,&l,&r);
    if(op[0] == 'C'){
      scanf("%d",&x);
      change(l,r,x);
    }
    else
      printf("%lld\n",ask(l,r));
  }
  return 0;
}

2 蒲公英 BZOJ2724

题目要求在线,我们同样将我们的序列分块,假设分成T块
预处理出所有块的端点两两之间的每个数出现的次数病记录众数,需要空间 O ( N T 2 ) O(N*T^2) O(NT2)
需要时间 O ( N T 2 ) O(N*T^2) O(NT2),每次询问,需要暴力修改 补足一块的部分 O ( M N / T ) O(M*N/T) O(MN/T),之后将修改补回去

const int N  = 40006,T = 37;
int a[N],b[N],L[N],R[N],pos[N];
int c[T][T][N],f[T][T][2],now[2];
inline void work(int x,int y,int num){
    ++c[x][y][num];
    if(c[x][y][num] > now[0] ||(c[x][y][num] == now[0] && num < now[1])){
        now[0] = c[x][y][num];
        now[1] = num;
    }
}   
int ask(int l,int r){
    int p = pos[l],q = pos[r];
    int x = 0,y = 0;
    if(p+1 <= q-1){
        x = p+1;
        y = q-1;
    }
    memcpy(now,f[x][y],sizeof(now));
    if(p == q){
        rep(i,l,r+1) work(x,y,a[i]);
        rep(i,l,r+1) --c[x][y][a[i]];
    }
    else{
        rep(i,l,R[p]+1) work(x,y,a[i]);
        rep(i,L[q],r+1) work(x,y,a[i]);
        rep(i,l,R[p]+1) --c[x][y][a[i]];
        rep(i,L[q],r+1) --c[x][y][a[i]];
    }
    return b[now[1]];
}
int main(void){
        // freopen("input.txt","r",stdin);

        // freopen("output1.txt","w+",stdout);
    int n,m;cin>>n>>m;
    rep(i,1,n+1) scanf("%d",&a[i]);
    memcpy(b,a,sizeof(a));
    sort(b+1,b+n+1);
    int tot = unique(b+1,b+n+1)-(b+1);
    rep(i,1,n+1) a[i] = lower_bound(b+1,b+tot+1,a[i])-b;
    int t = pow((double)n,(double)1/3);
    int len = t?n/t:n;
    for(int i = 1;i <= t; ++i){
        L[i] = (i-1)*len+1;
        R[i] = i*len;
    }
    if(R[t] < n){
        L[t+1] = R[t]+1;
        R[++t] = n;
    }
    rep(i,1,t+1)
        rep(j,L[i],R[i]+1)
            pos[j] = i;

    me(c),me(f);
    rep(i,1,t+1){
        rep(j,i,t+1){
            rep(k,L[i],R[j]+1)
                ++c[i][j][a[k]];
            rep(k,1,tot+1)
                if(c[i][j][k] > f[i][j][0]){
                    f[i][j][0] = c[i][j][k];
                    f[i][j][1] = k;
                }
        }
    }
    int x = 0;
    while(m--){
        int l,r;scanf("%d%d",&l,&r);
        l = (l+x-1)%n+1;
        r = (r+x-1)%n+1;
        if(l > r) swap(l,r);
        printf("%d\n",x = ask(l,r));
    }


    return 0;
}

3 磁力块

将磁力石按照重量分块,然后块内按照距离排序,用一个数组记录每段中吸引到何处,将所有吸引的磁石加入队列,然后去吸引每一个块,存在一个j,1…j-1的所有重量小于引力,j+1 之后的都不能吸引,于是可以扫描叫1…j-1 ,暴力扫描j块中的,之后将满足的都加入到队列里

const int maxn = 250000+10;
struct C{
   LL  x,y,m,p,r;
   LL d;
};
C c[maxn];
// int H[maxn];
int L[maxn],R[maxn],pos[maxn];
LL  maxm[maxn];
bool cmp1(const C &a,const C &b){
    return a.m < b.m;
}
bool cmp2(const C &a,const C &b){
    return a.d < b.d; 
}
bool inq[maxn];
int main(void)
{
        // freopen("input.txt","r",stdin);
        // freopen("output.txt","w+",stdout);
    LL x0,y0,pl,pr,N;cin>>x0>>y0>>pl>>pr>>N;
    pr *= pr;
    // cerr<<pr<<endl;
    rep(i,1,N+1){
        scanf("%lld %lld %lld %lld %lld",&c[i].x,&c[i].y,&c[i].m,&c[i].p,&c[i].r);
        c[i].x -= x0;
        c[i].y -= y0;
        c[i].d =  c[i].x*c[i].x+c[i].y*c[i].y;
        c[i].r *= c[i].r;
    }
    int t = sqrt(N);
    for(int i = 1;i <= t; ++i)
        L[i] = (i-1)*t+1,R[i] = i*t;
    if(R[t] < N){
        L[t+1] = R[t]+1;
        R[++t] = N;
    }
    // cout<<t<<endl;
    sort(c+1,c+N+1,cmp1);
    // cout<<L[1]<<" "<<R[1]<<endl;
    for(int i = 1;i <= t; ++i){
        sort(c+L[i],c+R[i]+1,cmp2);
        for(int j = L[i];j <= R[i]; ++j)
            maxm[i] = max(maxm[i],c[j].m);
        // cout<<i<<" "<<L[i] <<" "<<R[i]<<endl;
    }
    // for(int i = 1;i <= t; ++i){
    // cout<<endl;
    // for(int j = L[i];j <= R[i]; ++j){
    // cout<<c[j].m<<" "<<c[j].d<<" "<<c[j].r<<" "<<c[j].p<<endl;
    // }
    // }
    queue<P> Q;
    Q.push(P(pl,pr));
    int cnt = 0;
    while(!Q.empty()){
        P p = Q.front(); Q.pop();
        cnt++;
        int j = t+1;
        for(int i = 1;i <= t; ++i){
            if(maxm[i] > p.first){
                j = i;
                break;
            }
        }
        for(int i = 1;i < j; ++i)
        {
            while(L[i] <= R[i]&&c[L[i]].d <= p.second){
                if(!inq[L[i]])
                    Q.push(P(c[L[i]].p,c[L[i]].r));
                inq[L[i]] = true;
                L[i]++;

            }
        }
        if(j <= t){
            for(int i = L[j];i <= R[j]; ++i){
                if(c[i].m <= p.first &&c[i].d <= p.second){
                    if(!inq[i])
                        Q.push(P(c[i].p,c[i].r));
                    inq[i] = true;
                }
            }
        }
    }
    cout<<cnt-1<<endl;
   return 0;
}

4 洛谷P2709 小B的询问

对查询按照l排序,然后分块,块内按照r排序,暴力修改

const int maxn = 50000+10;
int n,m,k;
int pos[maxn];
int a[maxn];
int num[maxn];
LL  Ans[maxn];
int L[maxn],R[maxn];
struct Query{
    int l,r,id;
};
Query q[maxn];
bool cmp1 (const Query &a,const Query &b){
    return a.l < b.l ||(a.l == b.l && a.r < b.r);
}
bool cmp2(const Query &a,const  Query &b){
    return a.r < b.r;
}

void work(int x,LL &ans,int d){
    ans -= 1ll*num[x]*num[x];
    num[x] += d;
    ans += 1ll*num[x]*num[x];
}
int main(){
    cin>>n>>m>>k;
    rep(i,1,n+1) scanf("%d",&a[i]);
    rep(i,1,m+1){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id = i;
    }
    int t = sqrt(m);
    for(int i = 1;i <= t; ++i){
        L[i] = (i-1)*t;
        R[i] = i*t;
    }
    if(R[t] < m){
        L[t+1] = R[t]+1;
        R[++t] = m; 
    }
    sort(q+1,q+m+1,cmp1);
    for(int i = 1;i <= t; ++i){
        sort(q+L[i],q+R[i]+1,cmp2);
        LL ans = 0;
        me(num);
        int l = q[L[i]].l,r = q[L[i]].r;
        rep(i,l,r+1) work(a[i],ans,1);
        Ans[q[L[i]].id] = ans;
        for(int j = L[i]+1;j <= R[i]; ++j){
            // l = L[j].l,r = L[j].r;
            while(l < q[j].l) work(a[l++],ans,-1);
            while(l > q[j].l) work(a[--l],ans,1);
            while(r < q[j].r) work(a[++r],ans,1);
            while(r > q[j].r) work(a[r--],ans,-1);
            Ans[q[j].id] = ans;
        }
    }
    rep(i,1,m+1)
        printf("%lld\n",Ans[i]);
    return 0;
}

例题

1 小z的袜子(国家集训队) 对询问分块暴力修改
2 D. Powerful array 同上
3 HDU6333-2018ACM暑假多校联合训练1002-Harvest of Apples-莫队 组合数查询也可以通过莫队分块然后修改

全部评论

相关推荐

05-19 20:18
已编辑
长沙理工大学 Java
2025916Ney...:你能这时候知道要实习已经超过90%不要放弃
点赞 评论 收藏
分享
我曾经以为实习第一天离职,听上去很不可思议,甚至像是小说中的剧情,但是这种事情真的发生在了我身上。在5月23日,我参加了一个中小厂的面试,一面非常的顺利,大部分问题我都成功回答出来了,到了后面反问环节,还知道了面试官居然是我的学长,这让我非常亲切。当天hr就约我进行二面了,我当时感觉自己好幸运,好幸福。hr给我说二面的面试官可能会比较严厉,但是私下人很好(划重点!)时间来到了5月25日,我二面的日子,我家住重庆主城的南边,公司在北边,我单程的通勤时间是2h左右。二面很艰难,比一面难了许多,全是上一段实习的项目拷打和场景题,我当时压力很大,感觉很不适(可能是我没有被这么压力面过),但是我没有觉得面试官有什么问题,或者这个方式有什么不妥,我只是觉得自己还很菜,对于这方面的准备还需要加强。然后就在这样的煎熬中,我度过了二面。hr姐姐是个很好的人,她一直在帮我跟进二面的进度,也一直在给我反馈,在5月26日晚,hr给我说二面过了,让我准备学信网在线认证的资料等,我当时觉得那是我人生中最快乐的几个瞬间之一。5月26日到6月4日,可能是我大学生涯中过得最像一个普通的二本男大学生的日子:天天睡到自然醒,到了实验室就躺着玩手机,玩累了就晚电脑,然后隔三岔五就出去吃顿好的(当然,没有说这样的生活不好的意思),然后就到了今天,6月5日,我入职——和我离职的日子。6月5日,早上5:55,重庆很多高中走读生起床的时间,我也起床了,因为通勤需要两个小时,加上是入职的日子,我决定早点到公司。重庆的直快列车,早上是没有位置的,我背着我的游戏本站了一个多小时,加上步行1.5km,终于在8:27分到了公司大厅,然后在9:00过,被一个同事带到了工位上,工位左边的就是我的学长,右边的是一个大四的同事。由于我们的项目是银行的内网开发,需要安装一系列银行内网的软件和配置一系列的环境。两个同事都非常热心的一直在帮助我,终于在十点过,配置到了最后一步,安装银行的一个什么安全助手,安装后就出问题了:“我的conda被列为了高危软件,需要立即卸载”(虽然我不知道为什么conda是高危软件),但是我conda配置了很多虚拟环境,我不是很想删除。于是我和几个同事商量了之后,我决定使用公司电脑进行环境配置。但是现在有个严重的问题:“因为我的conda被列为了高危软件,导致银行的安全助手把我的网断了”(我不知道是什么原理,可以让我无法上网,请原谅我的垃圾计网),更逆天的是,这个安全助手一旦安装则无法卸载,并且永久启动。此时我的想法是&quot;我反正向公司申请了电脑,那得先把我的电脑搞好,把conda卸载了,先有网了再说。&quot;然后我就开始卸载conda,因为我的环境什么的很多,conda卸载得很慢,此时,二面面试官——也就是我们的项目经理,也就是这个故事的男二号他来了,他一进门就对着我说“你一天没得事干得迈?怎么坐起在耍哎?”我当时就懵了,我在等待conda卸载,此时我的电脑是没有网的,我什么也干不了,我只能盯着屏幕上面的进度条,不然我还可以耍手机。但是处于礼貌和下属的身份,我还是用认错的口吻回了一个“有事做,有事做”。本以为风波就会过去,但是我卸载conda后,软件依旧在报错,我的电脑依旧没有网。此时我的两个“同桌”仍然不厌其烦的帮我想办法解决,我的目光也就在他们两个的电脑上面来回跳动,这个时候,他又开始发狂了:“xx(我学长的名字),你没有给他安排任务吗?我感觉他一直没得事做得哎,你把下周要做的给他安排起啊!”,此时我已经有些厌烦,就没有理他,而我的学长非常耐心的给他解释了今天早上发生了什么事情和为什么我看起来无所事事。(真的感谢学长)但是搞了很久,还是没有解决这个问题,我们都有些无语了,特别是我,看到电脑被一个“流氓软件”搞得上不了网,就好像影视作品中无能的丈夫一样无力,我十分烦躁,此时,他点燃了我:“xxx(我的大名),下次就不可能让你因为自己的原因,上班来搞这些了哈,搞不好自己加班给我搞!”我当时就发火了,原因有两点:1、这根本不是我的问题啊,我怎么知道电脑里面有些看起来很日常的软件和内网的软件不兼容;2、我明明一直在解决问题,他什么都不知道但是却一直说我,还指着我说这些都来了。然后我就怼了回去,怼了他几句,他就说不出话了——“可能是不想和我计较吧,大概!”。中午吃饭的时候,整个项目组都很震惊,好像我是第一个怼他的,然后大家在一起吃饭的时候都在骂他,说他让整个团队变得非常压抑,他非常不讲道理,他就是这种人什么的。大家都在安慰我(这里非常感谢大家),但是我已经决定今天就离职!到了下班时间,气氛非常的微妙,大家都归心似箭,但是却无一人起身,这是为什么?——因为他要加班!是的,就是因为他要加班,没有一个人敢走!但是我已决心离职,于是收好东西之后,郑重的和我的学长还有旁边的同事告别后,扬长而去。到了地铁站我就给hr提出离职,hr主动和我打电话了解了情况,并且耐心的安慰我(这里非常感谢hr姐姐,如果她可以看到的话,衷心表示感谢);学长也给我发消息安慰我,让我冷静一下。现在,也已经深了,我相当的冷静,我还是决定离职,我不知道这是否是一个好的选择,至少在当下,大三的我觉得这是一个必要的,正确的选择!我也相当的后悔,我只是怼回去了,而我并没有骂他,相当的后悔!最后,再次衷心感谢hr姐姐、我的学长、我的同桌、和项目组中帮助过我的每一个人。(给大家一个面子,也不给大家找麻烦,我决定不曝光公司名和他的名字)
大三一定要找到实习:后悔啥呀,通勤2h➕第一天被疯狂压力➕加班,这日子后面会很难受
实习生的蛐蛐区
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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