莫队,是一种算法,是国家队长莫涛发明的orz,
它是来解决什么问题的呢?划重点
我们常常会遇到这样一类题:给你一个\([1,n]\)的序列,每次查询\([l,r]\)的一些信息(例如不同数的个数等),这个时候,我们就可以使用莫队来解决。
注意,莫队是一种离线算法。
我们考虑,当我们知道\([l1,r1]\)的值时,我们要计算出\([l2,r2]\)的值。
我们可以\(O(1)\)地算出\([l1-1,r1],[l1+1,r1],[l1,r1-1],[l1,r1+1]\)的值,那么,我们就可以在\(O(|l2-l1|+|r2-r1|)\)的时间复杂度内完成这次转移,即算出答案。也就是说,我们可以省去这两个询问区间的交集所需要的时间。
而我们现在要做的,就是通过一种特定的排序方式,使得对于一个询问集合\(q\),使得\(\sum_{i=1}^n(|q[i].l-q[i-1].l|+q[i].r-q[i-1].r|)\)达到我们可以接受的值;
我们考虑用分块来优化,以左端点所在的块为第一关键字,右端点的值为第二关键字来排序,这样的时间复杂度就降到了\(O(n\sqrt{n})\)
证明?我也不会啊
根据dllxl的认(ka)真(chang)教(ji)导(qiao),我们在comp函数中可以进行优化(奇偶块优化),即如果是奇数块就右端点升序排列,否则就降序排列:
inline bool comp(Node a,Node b)
{
return belong[a.l]^belong[b.l] ? belong[a.l] < belong[b.l] : belong[a.l]&1 ? a.r<b.r : a.r>b.r;
}
这样会快很多,还有就是block的大小跟代码速度有巨大关系,根据dllxl等人的不懈努力,确定\(block=n/(m*2/3)\)时,是最快的(目前);
讲讲题吧:
小Z的袜子
我们考虑对于一个区间\([l,r]\),设\(cnt[x]\)为此区间中颜色为x的个数,则选到相同袜子的情况有\(\sum C_{cnt[x]}^2\)种,而任意选两个,一共有\(C_{r-l+1}^2\)种选法,两者相除即为答案。
由公式
\(C_n^2=\frac{n!}{2!*(n-2)!}=\frac{n*(n-1)}{2}\)并整理得
设\(len=r-l+1\),则答案为
\(\frac{\sum cnt[x]^2-\sum cnt[x]}{len\times(len-1)}\)
\(=\frac{\sum cnt[x]^2-len}{len\times(len-1)}\)
而我们要维护的,就是\(\sum cnt[x]^2(x\in col(l,r))\);
用莫队:
考虑转移的时候,如果加入一个元素x,则\(ans+=cnt[x]*2+1,cnt[x]++\)
(因为\((cnt[x]+1)^2=cnt[x]^2+2*cnt[x]+1\))
同理,当删除一个元素x,则\(ans-=cnt[x]*2-1,cnt[x]--\)(因为\((cnt[x]-1)^2=cnt[x]^2-2*cnt[x]+1\))
我们可以写两个函数:
inline void add(int x) {ans+=cnt[x]*2+1,cnt[x]++;}
inline void del(int x) {ans-=cnt[x]*2-1,cnt[x]--;}
然后,我们每转移的时候:
while(q[i].l>l) del(a[l++]);
while(q[i].l<l) add(a[--l]);
while(q[i].r<r) del(a[r--]);
while(q[i].r>r) add(a[++r]);
再统计一下答案,约个分就OK了
注意:先处理第一组询问,\(l==r\)时要特判;
然后?然后好像就没了,完结撒花
上代码:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxN=1e5 + 100;
struct Node
{
int l,r,id;
}q[maxN+1];
int belong[maxN+1],block;
int a[maxN+1],n,m,l,r;
long long res[maxN+1][2],ans,cnt[maxN+1];
inline int read()
{
int num=0,f=1;
char ch=getchar();
while(!isdigit(ch)) {if(ch==‘-‘) f=-1; ch=getchar();}
while(isdigit(ch)) num=(num<<3)+(num<<1)+(ch^48),ch=getchar();
return num*f;
}
inline bool comp(Node a,Node b)
{
return belong[a.l]^belong[b.l] ? belong[a.l]<belong[b.l] : belong[a.l]&1 ? a.r<b.r : a.r>b.r;
}
inline void add(int x) {ans+=2*cnt[x]+1; cnt[x]++;}
inline void del(int x) {ans-=2*cnt[x]-1; cnt[x]--;}
inline long long gcd(long long x,long long y,long long r)
{
if(!r) return y;
return gcd(y,r,y%r);
}
inline void work(int x)
{
long long len=q[x].r-q[x].l+1,tmp=ans-len;
if(!tmp) {res[q[x].id][0]=0,res[q[x].id][1]=1; return;}
len=len*(len-1);
long long g=gcd(tmp,len,tmp%len);
res[q[x].id][0]=tmp/g,res[q[x].id][1]=len/g;
}
int main()
{
n=read(),m=read(); block=n/sqrt(m*2/3);
for(int i=1;i<=n;i++) a[i]=read(),belong[i]=(i-1)/block+1;
for(int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].id=i;
sort(q+1,q+m+1,comp);
for(int i=q[1].l;i<=q[1].r;i++) add(a[i]);
work(1);
l=q[1].l,r=q[1].r;
for(int i=2;i<=m;i++)
{
while(q[i].l>l) del(a[l++]);
while(q[i].l<l) add(a[--l]);
while(q[i].r<r) del(a[r--]);
while(q[i].r>r) add(a[++r]);
work(i);
}
for(int i=1;i<=m;i++) printf("%lld/%lld\n",res[i][0],res[i][1]);
return 0;
}
原文地址:https://www.cnblogs.com/cmwqf/p/10225283.html