P1494 [国家集训队]小Z的袜子

博客围绕莫队算法展开,莫队是支持询问、不支持修改的离线算法,通过对查询区间排序并由上一区间移动得到本区间解。解题难点有分块或更难算法、移动过程推导、区间抽象划分。本题只需统计区间平方和,给出答案公式,还提及分块排序及大小建议。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题目

思路

说来讽刺,这题居然是同学叫我做的水题
哪个水题是莫队啊
莫队,莫涛队长发明的一种优雅的暴力,尊称莫队
我有一个绝妙的暴力,可惜时限太小A不掉
莫队,是一种支持询问不支持修改的离线算法。
大概就是以某种方式排序所要查询的区间,并通过上一个区间的移动得到这个区间解的方法。
听起来很简单,但难点在于3个:

  1. 这个所谓的某种方式是分块或更难算法
  2. 移动的过程可能很难推导
  3. 区间可能需要抽象划分

这里的3问题不存在,2问题可以解决。
我们只需要统计区间(长度len)平方和(sum)即可,答案公式为:
s u m − l e n l e n ( l e n − 1 ) sum-len\over len(len-1) len(len1)sumlen
移动公式略。

分块的话其实就是正常分块,然后按左端点所在的块的先后排序,若相同,按右端点的值排序
这里分块的建议:

块的大小可以取n/sqrt(m*2/3)),比较快 ——by lxl

code:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
//When I wrote this code,God and I unterstood what was I doing 
struct f{
	int l,r,id;
	long long x,y;
} a[50010];
int n,color[50010],m,k,l=1,r,len,id[50010];
long long sum,s[50010];
inline int read()
{
    int ret,c,f=1;
    while (((c=getchar())> '9'||c< '0')&&c!='-');
    if (c=='-') f=-1,ret=0;
    else ret=c-'0';
    while ((c=getchar())>='0'&&c<='9') ret=ret*10+c-'0';
    return ret*f;
}
bool cmp(f a,f b)
{
	return id[a.l]==id[b.l]?a.r<b.r:a.l<b.l;
}
bool cmp2(f a,f b)
{
	return a.id<b.id;
}
void up(int x,int y)
{
	sum-=s[color[x]]*s[color[x]];
	s[color[x]]+=y;
	sum+=s[color[x]]*s[color[x]];
	return;
}
long long gcd(long long x,long long y)
{
	long long r=x%y;
	while (r)
	{
		x=y,y=r,r=x%y;
	}
	return y;
}
int main()
{
	n=read(),m=read();
	len=n/sqrt(m*2/3);
	for (int i=1;i<=n;i++)
	{
		color[i]=read();
		id[i]=(i-1)/len+1;
	}
	for (int i=1;i<=m;i++)
	{
		a[i].l=read(),a[i].r=read(),a[i].id=i;
	}
	sort(a+1,a+1+m,cmp);
	l=1,r=0;
	for (int i=1;i<=m;i++)
	{
		while (r<a[i].r)
		{
			up(r+1,1);
			r++;
		}
		while (r>a[i].r)
		{
			up(r,-1);
			r--;
		}
		while (l<a[i].l)
		{
			up(l,-1);
			l++;
		}
		while (l>a[i].l)
		{
			up(l-1,1);
			l--;
		}
		if (a[i].l==a[i].r)
		{
			a[i].x=0,a[i].y=1;
			continue;
		}
		a[i].x=sum-(a[i].r-a[i].l+1);
		a[i].y=(a[i].r-a[i].l+1)*1ll*(a[i].r-a[i].l);
		long long u=__gcd(a[i].x,a[i].y);
		a[i].x/=u,a[i].y/=u;
	}
	sort(a+1,a+1+m,cmp2);
	for (int i=1;i<=m;i++)
	{
		printf("%lld/%lld\n",a[i].x,a[i].y);
	}
	return 0; 
}
//Now,only God know
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值