学习博客:https://www.cnblogs.com/Paul-Guderian/p/6933799.html
适用范围:
离线,简单修改,O1或Ologn求附近区间
只有查询:
bzoj2038 莫队+概率+逆元 N种袜子,给若干区间问取到两只相同袜子的概率
sum(C(a,2))/C(区间长度,2)
维护sum[]每种袜子的数目,转移的时候直接减去整个a^2后在加上去
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=5e4+5;
struct mo{
ll l,r,id,a,b;
}q[maxn];
int bel[maxn];
int col[maxn];
ll sum[maxn];
ll ans=0;
bool cmp(mo a,mo b)
{
if(bel[a.l]==bel[b.l])return a.r<b.r;
return a.l<b.l;
}
bool cmp2(mo a,mo b){return a.id<b.id;}
inline ll read()
{
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void fuck(int idx,int add)
{
ans-=sum[idx]*sum[idx];
sum[idx]+=add;
ans+=sum[idx]*sum[idx];
}
int main()
{
int n,m;ans=0;
n=read();m=read();
int unit=sqrt(n);
for(int i=1;i<=n;i++)
{
col[i]=read();
bel[i]=i/unit+1;
}
for(int i=1;i<=m;i++)
{
q[i].l=read();q[i].r=read();q[i].id=i;
}
sort(q+1,q+1+m,cmp);
int L=1,R=0;
for(int i=1;i<=m;i++)
{
while(L<q[i].l)fuck(col[L],-1),L++;
while(L>q[i].l)fuck(col[L-1],1),L--;
while(R<q[i].r)fuck(col[R+1],1),R++;
while(R>q[i].r)fuck(col[R],-1),R--;
if(q[i].l==q[i].r)
{
q[i].a=0,q[i].b=1;
continue;
}
q[i].a=ans-(q[i].r-q[i].l+1);
q[i].b=(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
ll gcd=__gcd(q[i].a,q[i].b);
q[i].a/=gcd;q[i].b/=gcd;
}
sort(q+1,q+1+m,cmp2);
for(int i=1;i<=m;i++)
{
printf("%lld/%lld\n",q[i].a,q[i].b);
}
}
hdu3333 莫队+离散化 求区间l,r的加法和,相同的只加一次
hdu3874 莫队 求区间l,r的加法和,相同的只加一次
注意预处理离散化后的数组mp,每次都二分会T
#include<bits/stdc++.h>
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<math.h>
#define ll long long
using namespace std;
const int maxn=30005;
int bel[maxn];int col[30005];ll a[maxn];ll res[100005];int rk[30005],rn;
int mp[maxn];
ll ans=0;int n;int T;int l=1,r=0;int m;
struct mo{int id,l,r;}q[100005];
void setrk(int n){//调用前,所有y值被无序存入rk数组,下标为[1..rn]
rn=n;int idx=1;
sort(rk+1,rk+1+rn); //第一步排序
for(int i=2;i<=rn;++i) if(rk[i]!=rk[i-1]) rk[++idx]=rk[i]; //第二步去除重复值
rn=idx;
//此时,所有y值被从小到大无重复地存入rk数组,下标为[1..rn]
}
int getrk(int x) { return lower_bound(rk+1,rk+1+rn,x)-rk;}
bool cmp(mo a,mo b){ if(bel[a.l]==bel[b.l])return a.r<b.r;return a.l<b.l; }
bool cmp2(mo a,mo b){ return a.id<b.id; }
void revise(int x,int idx,int d){ col[mp[idx]]+=d; if(d>0)ans+=1ll*(col[mp[idx]]==1)*x; if(d<0)ans-=1ll*(col[mp[idx]]==0)*x;}
inline ll read(){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main()
{
T=read();
while(T--)
{
ans=0;l=1;r=0;
memset(col,0,sizeof(col));
n=read();int unit=(int)sqrt(n);
for(int i=1;i<=n;i++) a[i]=read(),rk[i]=a[i],bel[i]=i/unit+1;
m=read();setrk(n);
for(int i=1;i<=n;i++) mp[i]=getrk(a[i]);
for(int i=1;i<=m;i++) q[i].id=i,q[i].l=read(),q[i].r=read();
sort(q+1,q+1+m,cmp);
for(int i=1;i<=m;i++)
{
while(l<q[i].l)revise(a[l],l,-1),l++;
while(l>q[i].l)revise(a[l-1],l-1,1),l--;
while(r<q[i].r)revise(a[r+1],r+1,1),r++;
while(r>q[i].r)revise(a[r],r,-1),r--;
res[q[i].id]=ans;
//printf("test:%d\n",ans);
}
for(int i=1;i<=m;i++)
printf("%I64d\n",res[i]);
}
}
带单点修改
BZOJ2120
树上莫队
不会