普通莫队🤔
莫队:对于只有询问没有修改的问题,先将询问存储下来,对其进行排序,每次从上一询问区间暴力转移到下一询问区间。
复杂度:如果是\(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;
}