题意:n个数m次询问,问在区间[l,r]内任意抽取两个数,问两个数相等的概率。
分析:据说莫队算法可以离线解决一些区间不修改的问题。
emmmmm才发现自己写的东西乱码了额,看这篇博客http://www.cnblogs.com/MashiroSky/p/5914637.html
嗯哼,反正线段树是解决不了这个题。那么就用莫队算法离线处理了。
首先我们可以列个式子来计算概率 p =
分母毫无疑问可以直接算出来
考虑分子,
,所以分子=
显然
那么主要的问题就是
我们知道,离线算法是将某个已知区间[l,r]慢慢的扩展到[li,ri],先做一个假设,设[li,ri]为[l,r+1],那么整个区间也就多了一个元素x
对与
![](https://i-blog.csdnimg.cn/blog_migrate/604718beb43eee4539bac90af6f6e5c5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/eca0441909c07d966b37af84a32cdae2.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5946ac4ddf024c90425c78bbce6ceae4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/40827a6398fcec6b551562eca003ceb9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/29f7e8ac84ae7bdceeb58cf9bcd1af4f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/604718beb43eee4539bac90af6f6e5c5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/eca0441909c07d966b37af84a32cdae2.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5946ac4ddf024c90425c78bbce6ceae4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/40827a6398fcec6b551562eca003ceb9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/e2918013f651930d37d12461f46ef48e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/85915155db75c6cb652986c6e59d5f92.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c049603df3d9757c73b3fab9579429ab.png)
![](https://i-blog.csdnimg.cn/blog_migrate/69d2aad2eaa7f5eb043274d85727f6b5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/29f7e8ac84ae7bdceeb58cf9bcd1af4f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/14dfee35b05e4e5a4f35539d54abc9a9.png)
我们很容易计算新的S=S0-f(i)^2+(f(i)+1)^2=S0+2f(i)+1
不断的更新就可以算出来了
但是如果给的数据很坑的话,那就可能会TLE
这个时候我们就要利用莫队算法离线处理这个问题了
在使用莫队算法之前,我们首先可以对序列进行分块,这样可以O(n^1.5)查询(啊证明
某大佬O(n^1.5)的证明吼吼吼
分好块之后,先按l从小到大排序,如果l等的话,按r从小到大排序
然后更新完当前区间,再记录下结果就可以进行下一次查询了:)
参考代码:
/*分块和莫队算法*/
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long LL;
const int maxn = 1e5+10;
int n,m;
int col[maxn];//颜色序列
int pos[maxn];//pos[i]表示分块之后第i个位置在第多少个块里面
int f[maxn];//f[i]表示i出现的次数
LL gcd( LL a, LL b)
{
return (b==0) ? a : gcd(b,a%b);
}
LL sqr( LL x)
{
return x*x;
}
struct Query{
int l,r,id;
LL a,b;
bool operator < ( const Query &q)const
{
return l < q.l || ( l == q.l && r < q.r);
}
void modify()
{
LL k = gcd(a,b);
a /= k;
b /= k;
}
};
Query q[maxn];
bool cmp( Query q1, Query q2)
{
return q1.id < q2.id;
}
void Modify( int p, LL & ans, int add)
{
// ans += 2*add*f[col[p]]+1;
ans -= f[col[p]]*f[col[p]];
f[col[p]] += add;
ans += f[col[p]]*f[col[p]];
}
int main()
{
while( ~scanf("%d%d",&n,&m))
{
mem(f,0);
for( int i = 1; i <= n; i++)
scanf("%d",&col[i]);
int limit = (int)sqrt(double(n)+0.5);
for( int i = 1; i <= n; i++)
pos[i] = (i-1)/limit+1;//左端点分块
for( int i = 1; i <= m; i++)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id = i;
q[i].a = q[i].b = 0;
}
sort(q,q+1+m);
LL ans = 0;
int l = 1, r = 0;
for( int i = 1; i <= m; i++)
{
if( r < q[i].r)
{
for( r = r+1; r < q[i].r; r++)
Modify(r,ans,1);
Modify(r,ans,1);
}
if( q[i].l < l)
{
for( l = l-1; l > q[i].l; l--)
Modify(l,ans,1);
Modify(l,ans,1);
}
if( q[i].r < r)
{
for( ; r > q[i].r; r--)
Modify(r,ans,-1);
}
if( l < q[i].l)
{
for( ; l < q[i].l; l++)
Modify(l,ans,-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].modify();
}
sort(q,q+1+m,cmp);
for( int i = 1; i <= m; i++)
printf("%lld/%lld\n",q[i].a,q[i].b);
}
return 0;
}