记录一个菜逼的成长。。
转自参考博客
莫队算法可用于解决一类可离线且在得到区间
[l,r]
的答案后,能在
O(1)
或
O(logn2)
得到区间
[l,r+1]
或
[l−1,r]
的答案的问题
问题:
给出n个数字,m次询问,每次询问在区间
[li,ri]
之间任选两个数字相等的概率是多少。(n,q<=50000)
在区间
[l,r]
中,这个概率是:
∑vi=1C(2,f(i))C(2,r−l+1)=∑vi=1f(i)2−∑vi=1f(i)(r−l+1)∗(r−l)
(v表示数字值,f(i)表示数字i在区间内出现的次数)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 50000 + 10;
int pos[maxn],f[maxn];
struct Node{
int l,r,id;
LL a,b;
bool operator < (const Node &a) const{
if(pos[l] != pos[a.l])return pos[l] < pos[a.l];
return r < a.r;
}
void update()
{
LL gcd = __gcd(a,b);
a /= gcd;b /= gcd;
}
}Q[maxn];
int a[maxn];
bool cmp(Node a,Node b)
{
return a.id < b.id;
}
void update(LL &ans,int ind,int v)
{
ans = ans + 2 * v * f[a[ind]] + 1;
f[a[ind]] += v;
}
void solve(int n,int m)
{
int l = 1,r = 0;
LL ans = 0;
for( int i = 1; i <= m; i++ ){
if(r < Q[i].r){
for( r = r + 1; r < Q[i].r; r++ )
update(ans,r,1);
update(ans,r,1);
}
if(l > Q[i].l){
for( l = l - 1; l > Q[i].l; l-- ){
update(ans,l,1);
}
update(ans,l,1);
}
if(r > Q[i].r){
for( ; r > Q[i].r; r-- )
update(ans,r,-1);
}
if(l < Q[i].l){
for( ; l < Q[i].l; l++ )
update(ans,l,-1);
}
if(Q[i].l == Q[i].r){
Q[i].a = 0;
Q[i].b = 1;
continue;
}
Q[i].a = ans - (Q[i].r - Q[i].l + 1),Q[i].b = (LL)(Q[i].r - Q[i].l + 1) * (Q[i].r - Q[i].l);
Q[i].update();
}
sort(Q+1,Q+1+m,cmp);
for( int i = 1; i <= m; i++ ){
printf("%lld/%lld\n",Q[i].a,Q[i].b);
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for( int i = 1; i <= n; i++ )
scanf("%d",a+i);
int k = (int)(sqrt(n));
for( int i = 1; i <= n; i++ ){
pos[i] = (i - 1) / k + 1;
}
for( int i = 1; i <= m; i++ ){
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id = i;
}
sort(Q+1,Q+1+m);
solve(n,m);
return 0;
}