码迷,mamicode.com
首页 > 其他好文 > 详细

[知识点]莫队大法好

时间:2017-09-23 18:56:40      阅读:187      评论:0      收藏:0      [点我收藏+]

标签:int   div   bool   相同   for   排序   高效   复杂   随机   

昨天的模拟赛T3是个非常恶心的东东,不过今天获知有人莫队+O3卡常A了==

但是原来OD表示莫队目前没什么用不用学,就没学过,所以昨天无奈不会打

但是今天哼哼,学会了这个新的知识点,而且觉得还是挺好用的啊

毕竟暴力是哪里都能用得上的。。。


所谓莫队,是一个优雅的暴力

解决的问题就是离线区间询问,好像是无敌的。(然而区间修改我目前还不会,而且区间最值好像也不会,而且区间最值也不用莫队233)

首先我们知道区间[L,R],那么我们一定能暴力求出来[L‘,R‘]。我们将所有询问存起来,暴力求出来第一个区间,然后用这个区间去更新下一个区间(其实就是相当于不用算两区间重合的部分了)

为了我们暴力求的不重合的区间尽量少,我们会选择排序。那么如何排序呢?肯定不能严格按第一关键左端点升序,第二关键右端点升序来搞。对于随机数据这样还好,但是对于出题人存心卡你的数据效率就又降下来了。比如区间[1,100]推区间[2,3],肯定会很慢。

所以为了解决这个问题,我们需要让左端点“大概”呈上升趋势,然后搞右端点(就是让两个区间之间离得近)。于是出现了最小曼哈顿距离生成树(然而我们并不用它233)

有一个优美的方法那就是另一个暴力——分块大法!

当然我们这里只需要分块中的块就好啦,我们把所有询问区间以左端点的块升序排序,如果相同就以右端点(它本身)排序,这样就能提高效率

时间复杂度大概是O(n*√n)  (拒绝证明因为不会)

然后我们只需要循环一遍,用上一个区间求这个区间(暴力更新不重合部分即可),然后储存答案最后输出即可。


上例题:[SDOI2009]HH的项链 比较裸的莫队了

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 210000
#define LL long long
int a[N];
int n,m,k;
int match[N],len,cnt[1001000];
LL ans[N];
struct haha{
    int l,r,id;
}edge[N];
bool cmp(const haha &a,const haha &b){
    if(match[a.l]==match[b.l]) return a.r<b.r;
    return match[a.l]<match[b.l];
}
void add(int i,int num){
    if(cnt[num]==0) ans[i]++;
    cnt[num]++;
}
void erase(int i,int num){
    cnt[num]--;
    if(cnt[num]==0) ans[i]--;
}
int main(){
    scanf("%d",&n);
    len=(int)sqrt(n+0.5);
    //cout<<len<<endl;
    pos(i,1,n) scanf("%d",&a[i]),k=max(a[i],k);
    pos(i,1,n){
        match[i]=(i-1)/(len+1);
    }
    scanf("%d",&m);
    pos(i,1,m){
        scanf("%d%d",&edge[i].l,&edge[i].r);
        edge[i].id=i;
    }
    sort(edge+1,edge+m+1,cmp);
    int lastl,lastr;
    lastl=edge[1].l;lastr=edge[1].r;
    pos(i,lastl,lastr){
        cnt[a[i]]++;
    }
    
    pos(i,0,k){
        if(cnt[i]!=0)  ans[edge[1].id]++;
    }
    
    pos(i,2,m){
        ans[edge[i].id]=ans[edge[i-1].id];
        int nowl=edge[i].l,nowr=edge[i].r;
        if(nowl>=lastl) pos(j,lastl,nowl-1)  erase(edge[i].id,a[j]);
        else  pos(j,nowl,lastl-1)  add(edge[i].id,a[j]);
        
        if(nowr>=lastr)  pos(j,lastr+1,nowr)  add(edge[i].id,a[j]);
        else  pos(j,nowr+1,lastr) erase(edge[i].id,a[j]);
        
        lastr=nowr;lastl=nowl;
        
    }
    pos(i,1,m) printf("%lld\n",ans[i]);
    return 0;
} 

  


 

好啦新技能 莫队 get√

 

[知识点]莫队大法好

标签:int   div   bool   相同   for   排序   高效   复杂   随机   

原文地址:http://www.cnblogs.com/Hallmeow/p/7581798.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!