了解学习了一下莫队,果然是个很好用的东西啊,我为什么现在才学…
用途
处理一些离线的区间问题
一般来说,令 ansi,j 表示询问区间 [i,j] 的答案,则 ansi,j 可以在常数的复杂度内由 ansi−1,j 或 ansi+1,j 或 ansi,j−1 或 ansi,j+1 推得
例题
2038: [2009国家集训队]小Z的袜子(hose)
简单推一下可以发现很好转移,对于移动的那一端只要在分子里修改:把C(2,i)改成C(2,i+1||i-1)就行了
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 50005
#define LL long long
using namespace std;
struct node{
int l,r,id;
LL a,b;
}a[N];
int n,m,c[N],pos[N];
LL s[N],ans;
bool cmp1(node a,node b){
return (pos[a.l]<pos[b.l])||(pos[a.l]==pos[b.l]&&(pos[a.l]&1?a.r<b.r:a.r>b.r));
}
bool cmp2(node a,node b){return a.id<b.id;}
LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
inline LL pow(LL x){return x*x;}
void update(int x,int opt){
ans-=pow(s[c[x]]);
s[c[x]]+=opt;
ans+=pow(s[c[x]]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&c[i]);
int block=int(sqrt(n));
for(int i=1;i<=n;++i)
pos[i]=(i-1)/block+1;
for(int i=1;i<=m;++i) scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i;
sort(a+1,a+m+1,cmp1);
for(int i=1,l=1,r=0;i<=m;++i){
for(;r<a[i].r;++r) update(r+1,1);
for(;r>a[i].r;--r) update(r,-1);
for(;l<a[i].l;++l) update(l,-1);
for(;l>a[i].l;--l) update(l-1,1);
if(a[i].l==a[i].r){
a[i].a=0,a[i].b=1;
continue;
}
a[i].a=ans-(a[i].r-a[i].l+1);
a[i].b=1ll*(a[i].r-a[i].l+1)*(a[i].r-a[i].l);
LL tmp=gcd(a[i].a,a[i].b);
a[i].a/=tmp,a[i].b/=tmp;
}
sort(a+1,a+m+1,cmp2);
for(int i=1;i<=m;++i)
printf("%lld/%lld\n",a[i].a,a[i].b);
return 0;
}
3781: 小B的询问
区间头上加减一下平方就好了
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 50005
#define LL long long
using namespace std;
struct query{
int l,r,id;
}q[N];
int n,m,k,a[N],c[N],bl[N];
LL res,ans[N];
inline int read(){
int a=0;char f=1,c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
inline LL pow(LL x){return x*x;}
bool cmp(query a,query b){
return (bl[a.l]<bl[b.l])||(bl[a.l]==bl[b.l]&&((bl[a.l]&1)?a.r<b.r:a.r>b.r));
}
inline void update(int x,int add){
res-=pow(c[a[x]]);
c[a[x]]+=add;
res+=pow(c[a[x]]);
}
int main(){
n=read(),m=read(),k=read();
int sz=sqrt(n);
for(int i=1;i<=n;++i) a[i]=read(),bl[i]=(i-1)/sz+1;
for(int i=1;i<=m;++i){
q[i].l=read(),q[i].r=read();
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
for(int i=1,l=1,r=0;i<=m;++i){
for(;r<q[i].r;++r) update(r+1,1);
for(;r>q[i].r;--r) update(r,-1);
for(;l<q[i].l;++l) update(l,-1);
for(;l>q[i].l;--l) update(l-1,1);
ans[q[i].id]=res;
}
for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
return 0;
}
4542: [Hnoi2016]大数
结论:设
ai
为第
i
位及之后的数构成的数,如121121中,
题目没给模数范围,其实是超int了,所以离散处理一下
对于
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100005
#define LL long long
using namespace std;
struct query{
int l,r,id;
LL ans;
}q[N];
int n,m,s[N],num,bl[N],c[N];
LL a[N],b[N],pw[N],P,ans;
bool cmp(query a,query b){return (bl[a.l]<bl[b.l])||(bl[a.l]==bl[b.l]&&(bl[a.l]&1?a.r<b.r:a.r>b.r));}
bool cmp1(query a,query b){return a.id<b.id;}
void update(int x,int add){
if(a[b[x]]>1) ans-=(a[b[x]]-1)*a[b[x]]/2;
a[b[x]]+=add;
if(a[b[x]]>1) ans+=(a[b[x]]-1)*a[b[x]]/2;
}
int main(){
scanf("%lld",&P);
char ch[N];
scanf("%s",ch+1);
n=strlen(ch+1);
for(int i=1;i<=n;++i) s[i]=ch[i]-'0',pw[i]=i==1?1:(pw[i-1]*10)%P;
LL tmp=0;
for(int i=n;i>=1;--i){
tmp=(tmp+pw[n-i+1]*s[i])%P;
a[i]=b[i]=tmp;
}
sort(a+1,a+n+1);
num=unique(a+1,a+n+1)-(a+1);
for(int i=1;i<=n+(a[1]==0);++i){
b[i]=lower_bound(a+1,a+num+1,b[i])-a;
c[i]=a[i];
}
memset(a,0,sizeof(a));
int sz=sqrt(n);
for(int i=1;i<=n;++i) bl[i]=(i-1)/sz+1;
scanf("%d",&m);
for(int i=1;i<=m;++i)
scanf("%d%d",&q[i].l,&q[i].r),++q[i].r,q[i].id=i;
sort(q+1,q+m+1,cmp);
if(P==2||P==5){
for(int i=1;i<=n;++i)
if(s[i]%P==0) a[i]=i,b[i]=1;
else a[i]=b[i]=0;
for(int i=1;i<=n;++i) a[i]+=a[i-1],b[i]+=b[i-1];
for(int i=1;i<=m;++i) --q[i].r,q[i].ans=a[q[i].r]-a[q[i].l-1]-(b[q[i].r]-b[q[i].l-1])*(q[i].l-1);
}
else
for(int i=1,l=1,r=0;i<=m;++i){
for(;r<q[i].r;++r) update(r+1,1);
for(;r>q[i].r;--r) update(r,-1);
for(;l<q[i].l;++l) update(l,-1);
for(;l>q[i].l;--l) update(l-1,1);
q[i].ans=ans;
}
sort(q+1,q+m+1,cmp1);
for(int i=1;i<=m;++i)
printf("%d\n",q[i].ans);
return 0;
}
2120: 数颜色
这道题做法很多,我用分块也写过,还没有用数据结构写过…
用莫队做的话就是维护一下每个询问操作之前最近的一个修改设为 pre
然后正常莫队过程中对于每一个询问,只要把修改操作通过执行或撤销,使目前的状态就是询问时的状态即可
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 10005
#define M 1000005
using namespace std;
struct C{
int pos,c;
}c[N];
struct Q{
int l,r,id,lst;
}q[N];
int n,m,m0,m1,res,l,r,a[N],bl[N],mark[M],ans[N];
inline int read(){
int a=0;char f=1,c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}
return a*f;
}
inline bool cmp(Q a,Q b){
return bl[a.l]<bl[b.l]||(bl[a.l]==bl[b.l]&&((bl[a.l]&1)?a.r<b.r:a.r>b.r));
}
inline void change(C &x){
if(x.pos>=l&&x.pos<=r){
if(--mark[a[x.pos]]==0) --res;
if(++mark[x.c]==1) ++res;
}
swap(a[x.pos],x.c);
}
inline void update(int x,int add){
if(add==1) if(++mark[a[x]]==1) ++res;
if(add==-1) if(--mark[a[x]]==0) --res;
}
int main(){
n=read(),m=read();
int sz=sqrt(n);
for(int i=1;i<=n;++i) a[i]=read(),bl[i]=(i-1)/sz+1;
for(int i=1;i<=m;++i){
char ch=getchar();
while(ch!='Q'&&ch!='R')
ch=getchar();
int x=read(),y=read();
if(ch=='R') c[++m0]=(C){x,y};
else q[++m1]=(Q){x,y,i,m0}; //m1之前有m0次修改
}
sort(q+1,q+m1+1,cmp);
int nw=0;
l=1,r=0;
for(int i=1;i<=m1;++i){
for(;nw<q[i].lst;++nw) change(c[nw+1]);
for(;nw>q[i].lst;--nw) change(c[nw]);
for(;r<q[i].r;++r) update(r+1,1);
for(;r>q[i].r;--r) update(r,-1);
for(;l<q[i].l;++l) update(l,-1);
for(;l>q[i].l;--l) update(l-1,1);
ans[q[i].id]=res;
}
for(int i=1;i<=m;++i)
if(ans[i]) printf("%d\n",ans[i]);
return 0;
}