[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操作的实现步骤:

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值