题目
思路
说来讽刺,这题居然是同学叫我做的水题
哪个水题是莫队啊
莫队,莫涛队长发明的一种优雅的暴力,尊称莫队
我有一个绝妙的暴力,可惜时限太小A不掉
莫队,是一种支持询问不支持修改的离线算法。
大概就是以某种方式排序所要查询的区间,并通过上一个区间的移动得到这个区间解的方法。
听起来很简单,但难点在于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(len−1)sum−len
移动公式略。
分块的话其实就是正常分块,然后按左端点所在的块的先后排序,若相同,按右端点的值排序
这里分块的建议:
块的大小可以取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