莫队这东西其实就是一种优化了的暴力,具体原理很好理解,不用我吹,复杂度证明也不用我吹。
NO.1:小Z的袜子
如题是一道水题,其实是莫队经典题,莫队就是出自这道题的。这道题是当年莫涛队长出的一道题,是第一道莫队题。
如果你会莫队就一定会,代码如下:
#include"cstdio"
#include"cmath"
#include"algorithm"
#include"cstring"
#include"iostream"
#define LL long long
#define Rep(i,n,x) for(i=x;i<=n;i++)
using namespace std;
struct mes
{
LL l,r,ans1,ans2,dir;
};
mes c[50001];
LL a[50001],b[50001];
int k;
inline LL read()
{
char c=getchar();int x=0;
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}
bool cmp1(mes x,mes y)
{
return ((x.l/k<y.l/k)||((x.l/k==y.l/k)&&(x.r<y.r)));
}
bool cmp2(mes x,mes y)
{
return x.dir<y.dir;
}
inline void gcd(LL &x,LL &y)
{
if(y==0)x=0,y=1;
else
{
int p1=x,p2=y;
if(p1<p2)swap(p1,p2);
while(p1!=0&&p2!=0)
{
p1=p1%p2;
if(p1<p2)swap(p1,p2);
}
int p=p1?p1:p2;
x/=p;y/=p;
}
}
int main()
{
int n=read(),m=read();
k=sqrt(n);
int i;
Rep(i,n,1)
b[i]=read();
LL l=1,r=0;
Rep(i,m,1)
{
c[i].l=read(),c[i].r=read();
c[i].dir=i;
}
sort(c+1,c+m+1,cmp1);
memset(a,0,sizeof(a));
LL ans=0;
Rep(i,m,1)
{
while(l>c[i].l){
l--;
ans+=a[b[l]];
a[b[l]]++;
}
while(r<c[i].r){
r++;
ans+=a[b[r]];
a[b[r]]++;
}
while(l<c[i].l){
a[b[l]]--;
ans-=a[b[l]];
l++;
}
while(r>c[i].r){
a[b[r]]--;
ans-=a[b[r]];
r--;
}
c[i].ans1=ans;
c[i].ans2=(r-l+1)*(r-l)/2;
gcd(c[i].ans1,c[i].ans2);
}
sort(c+1,c+m+1,cmp2);
Rep(i,m,1)
printf("%lld/%lld\n",c[i].ans1,c[i].ans2);
return 0;
}
第一次打莫队码风似乎有点骚。还有由于BZOJ很爆炸所以为了大家浏览方便些接了洛谷的链接。
NO.2:小B的询问
比小Z的袜子水,就更不用解说了。
代码如下:
#include"cstdio"
#include"algorithm"
#include"iostream"
#include"cmath"
#include"cstring"
#define LL long long
#define MAXN 50000+10
#define Rep(i,n,x) for(i=x;i<=n;i++)
using namespace std;
inline LL read()
{
char c=getchar();LL x=0;
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}
struct asks
{
LL l,r,NO,ans;
};
asks a[MAXN];
LL num[MAXN],cnt[MAXN],n,m,k,p;
bool cmp1(asks x,asks y)
{
return ((x.l/p<y.l/p)||((x.l/p==y.l/p)&&(x.r<y.r)));
}
bool cmp2(asks x,asks y)
{
return x.NO<y.NO;
}
int main()
{
n=read(),m=read(),k=read();
p=sqrt(n);
int i;
Rep(i,n,1)
num[i]=read();
Rep(i,m,1)
{
a[i].l=read(),a[i].r=read();
a[i].NO=i;
}
sort(a+1,a+m+1,cmp1);
memset(cnt,0,sizeof(cnt));
LL l=1,r=0,ans=0;
Rep(i,m,1)
{
while(l>a[i].l)
{
l--;
ans+=cnt[num[l]]*2+1;
cnt[num[l]]++;
}
while(r<a[i].r)
{
r++;
ans+=cnt[num[r]]*2+1;
cnt[num[r]]++;
}
while(l<a[i].l)
{
ans-=cnt[num[l]]*2-1;
cnt[num[l]]--;
l++;
}
while(r>a[i].r)
{
ans-=cnt[num[r]]*2-1;
cnt[num[r]]--;
r--;
}
a[i].ans=ans;
}
sort(a+1,a+m+1,cmp2);
Rep(i,m,1)
printf("%lld\n",a[i].ans);
return 0;
}
NO.3:数颜色
一道待修改的莫队,其实就是比普通莫队多加一维,这道题难度比上道高些。
#include"cstdio"
#include"algorithm"
#include"cmath"
#include"iostream"
#define Rep(i,n,x) for(i=x;i<=n;i++)
#define MAXN 10000+10
using namespace std;
struct asks
{
int l,r,time,ans,dir;
};
asks a[MAXN];
int num[MAXN],anses[MAXN],n,m,p;
int color[1000001];
struct change
{
int place,next;
};
change b[1002];
inline int read()
{
char c=getchar();int x=0;
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}
bool cmp(asks x,asks y)
{
return ((x.l/p<y.l/p)||((x.l/p==y.l/p)&&(x.r/p<y.r/p))||((x.l/p==y.l/p)&&(x.r/p==y.r/p)&&(x.time<y.time)));
}
int turn(int now,int l,int r,int &ans)
{
if(b[now].place>=l&&b[now].place<=r)
{
color[num[b[now].place]]--;
if(color[num[b[now].place]]==0)
ans--;
color[b[now].next]++;
if(color[b[now].next]==1)
ans++;
}
swap(num[b[now].place],b[now].next);
}
int main()
{
n=read(),m=read();
int i,j;
Rep(i,n,1)
num[i]=read();
int v=1,k=0;
Rep(i,m,1)
{
char p1=getchar();
while(p1!='Q'&&p1!='R')p1=getchar();
if(p1=='Q')
{
a[++k].l=read();
a[k].r=read();
a[k].time=v;
a[k].dir=k;
}
else
{
v++;
b[v].place=read();
b[v].next=read();
}
}
p=sqrt(n);
sort(a+1,a+k+1,cmp);
int l=1,r=0,t=1,ans=0;
Rep(i,k,1)
{
while(a[i].l<l){l--;color[num[l]]++;if(color[num[l]]==1)ans++;}
while(r<a[i].r){r++;color[num[r]]++;if(color[num[r]]==1)ans++;}
while(l<a[i].l){color[num[l]]--;if(color[num[l]]==0)ans--;l++;}
while(r>a[i].r){color[num[r]]--;if(color[num[r]]==0)ans--;r--;}
while(t<a[i].time){t++;turn(t,l,r,ans);}
while(t>a[i].time){turn(t,l,r,ans);t--;}
anses[a[i].dir]=ans;
}
Rep(i,k,1)
printf("%d\n",anses[i]);
return 0;
}
NO.4:SDOI2009 HH的项链
naive,还可以用主席树来做,虽然很想用主席树来做但我还是忍住了(大雾)。
代码如下:
#include"cstdio"
#include"cmath"
#include"algorithm"
#define MAXN 1000001
#define Rep(i,n,x) for(i=x;i<=n;i++)
using namespace std;
struct ask
{
int l,r,dis;
};
inline int read()
{
char c=getchar();int x=0;
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}
ask asks[200001];
int color[MAXN];
int num[50001],anses[200001],p;
bool cmp(ask x,ask y)
{
return ((x.l/p<y.l/p)||(x.l/p==y.l/p&&x.r<y.r));
}
int main()
{
int n=read(),m;
p=sqrt(n);
int i;
Rep(i,n,1)
num[i]=read();
m=read();
Rep(i,m,1)
asks[i].l=read(),asks[i].r=read(),asks[i].dis=i;
sort(asks+1,asks+m+1,cmp);
int l=1,r=0,ans=0;
Rep(i,m,1)
{
while(l>asks[i].l){l--;color[num[l]]++;if(color[num[l]]==1)ans++;}
while(r<asks[i].r){r++;color[num[r]]++;if(color[num[r]]==1)ans++;}
while(l<asks[i].l){color[num[l]]--;if(color[num[l]]==0)ans--;l++;}
while(r>asks[i].r){color[num[r]]--;if(color[num[r]]==0)ans--;r--;}
anses[asks[i].dis]=ans;
}
Rep(i,m,1)
printf("%d\n",anses[i]);
return 0;
}