学习博客: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
树上莫队
不会