普通莫队
普通莫队🤔
莫队:对于只有询问没有修改的问题,先将询问存储下来,对其进行排序,每次从上一询问区间暴力转移到下一询问区间。
复杂度:如果是\(O(1)\)转移,则该算法总复杂度为\(O(n\sqrt n)\)。
排序方法:以\(l\)所在块编号为第一关键字,\(r\)为第二关键字排序。
例题:P1494.小Z的袜子
// Problem: P1494 [国家集训队] 小 Z 的袜子
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P1494
// Memory Limit: 128 MB
// Time Limit: 200000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<cstdio>
#include<math.h>
#include<algorithm>
#define int long long
using namespace std;
const int mn=50005;
int n,m,c[mn],sz;//sz是块长
int ll[mn],rr[mn],ans[mn];//开三个额外数组记录编号为i的询问对应区间、可行方案总数
int l,r,sum,s[mn];//当前左端点,右端点,可行方案总数,s[i]表示区间内含s[i]个种类为i的袜子
struct node
{
int l,r,id;//左端点、右端点、问题编号
}q[mn];
bool cmp1(node x,node y)
{
if((x.l-1)/sz==(y.l-1)/sz)return x.r<y.r;//以左端点所在块为第一关键字
return x.l<y.l;
}
int gcd(int x,int y)
{
if(y==0)return x;
return gcd(y,x%y);
}
void add(int x)
{
sum-=s[c[x]]*(s[c[x]]-1)/2;
s[c[x]]++;
sum+=s[c[x]]*(s[c[x]]-1)/2;
}
void del(int x)
{
sum-=s[c[x]]*(s[c[x]]-1)/2;
s[c[x]]--;
sum+=s[c[x]]*(s[c[x]]-1)/2;
}
signed main()
{
scanf("%lld%lld",&n,&m);
sz=sqrt(n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&c[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&q[i].l,&q[i].r);
q[i].id=i;
ll[i]=q[i].l;//预处理,防止后面再排一次序
rr[i]=q[i].r;
}
sort(q+1,q+m+1,cmp1);
l=1;
for(int i=1;i<=m;i++)
{
// printf("%d\n",sum);
while(l<q[i].l)del(l++);
while(l>q[i].l)add(--l);
while(r<q[i].r)add(++r);
while(r>q[i].r)del(r--);
//add和del操作
ans[q[i].id]=sum;
}
for(int i=1;i<=m;i++)
{
if(ll[i]==rr[i])
{
printf("0/1\n");//特判l=r情况
continue;
}
l=(rr[i]-ll[i]+1)*(rr[i]-ll[i])/2;
r=gcd(ans[i],l);
printf("%lld/%lld\n",ans[i]/r,l/r);//化成最简分数形式
}
return 0;
}

浙公网安备 33010602011771号