天天看点

[BZOJ 2038]小Z的袜子 莫队(Mo's Algorithm)模板题

今天总算撸懂了莫队

贴一个模板题代码 小Z的袜子

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
const int max_n=500005; 
int n,m,size;
int hose[max_n],pos[max_n];
long long tot[max_n];
class Query
{
	public:
		int l;int r;int id;long long a,b;
}q[max_n];
bool cmp(Query a,Query b)
{
	if(pos[a.l]==pos[b.l]) return a.r<b.r;
	else return pos[a.l]<pos[b.l];
}
int L=1,R=0;
long long ans=0;
void change(int val,int cl)
{
	ans-=tot[cl]*tot[cl];
	tot[cl]+=val;
	ans+=tot[cl]*tot[cl];
}
long long gcd(long long a,long long b) { return b==0?a:gcd(b,a%b); }
void work()
{
	for(register int i=1;i<=m;i++)
	{
		if(q[i].l==q[i].r) q[i].a=0,q[i].b=1;
		else
		{
			while(L<q[i].l) change(-1,hose[L]),L++;
			while(L>q[i].l) L--,change(1,hose[L]);
			while(R<q[i].r) R++,change(1,hose[R]);
			while(R>q[i].r) change(-1,hose[R]),R--;
			q[i].a=ans-(q[i].r-q[i].l+1);q[i].b=(long long)(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
			long long k=gcd(q[i].a,q[i].b);
			q[i].a/=k;q[i].b/=k;
		}
	}
}
bool outcmp (Query a ,Query b) { return a.id<b.id;}
void output()
{
	sort(q+1,q+1+m,outcmp);
	for(register int i=1;i<=m;i++)
		printf("%lld/%lld\n",q[i].a,q[i].b);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;i++)
		scanf("%d",&hose[i]);
	size=(int)sqrt(n+0.5);
	for(register int i=0;i<=n;i++)
		pos[i]=i/size;
	for(register int i=1;i<=m;i++)
		scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
	sort(q+1,q+1+m,cmp);
	work();
	output();
	return 0;
}
           

自己写了一种比较舒服的区间增减

如果当前已知区间的左端点小于询问区间的左端点,则需要将当前已知区间的左端点向右移。每次右移之前都需要先减去当前点对答案的影响。

如果当前已知区间的左端点大于询问区间的左端点,则需要将当前已知区间的左端点向左移。每次左移之后都需要再加上当前点对答案的影响。

右区间同理

while(L<Q.L) Modify(L,-1),L++;
while(L>Q.L) L--,Modify(L,1);
while(R<Q.R) R++,Modify(R,1);
while(R>Q.R) Modify(R,-1),R--;
           

Modify操作的实现步骤:

在答案中减去当前状态未被修改时对答案的贡献,进行修改操作后,再在答案中加上该状态被修改后对答案的新计算出的贡献。

继续阅读