昨天的模拟赛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√
时间: 2024-10-30 01:59:26