题意
给出n个数以及m个区间,求在每个区间内选出两个数,有多大的概率使选出的两个数相等。
solution
对于区间(l,r)的询问。
设其中颜色为x,y,z的袜子的个数为a,b,c...
那么答案即为 (a∗(a−1)/2+b∗(b−1)/2+c∗(c−1)/2....)/((R−L+1)∗(R−L)/2)
化简得: (a^2+b^2+c^2+...x^2-(a+b+c+d+.....))/((R-L+1)*(R-L))
即:(a^2+b^2+c^2+...x^2-(R-L+1))/((R-L+1)*(R-L))
我们需要解决的一个问题
求一个区间内每种颜色数目的平方和。
所以这道题目的关键是求一个区间内每种颜色数目的平方和。
但问题时怎么快速求解呢?
对于一般区间维护类问题一般想到用线段树。但是这题完全不知道线段树怎么做,所以只能用莫队算法了。
莫队算法是离线处理一类区间不修改查询类问题的算法。就是如果你知道了[L,R]的答案。你可以在O(1)的时间下得到[L,R−1]和[L,R+1]和[L−1,R]和[L+1,R]的答案的话。就可以使用莫队算法。
对于莫队算法其实就是暴力。只是预先知道了所有的询问。可以合理的组织计算每个询问的顺序以此来降低复杂度。要知道我们算完[L,R]的答案后现在要算[L′,R′]的答案。由于可以在O(1)的时间下得到[L,R−1]和[L,R+1]和[L−1,R]和[L+1,R]的答案。所以计算[L′,R′]的答案花的时间为|L−L′|+|R−R′|。如果把询问[L,R]看做平面上的点a(L,R);询问[L′,R′]看做点b(L′,R′)的话。那么时间开销就为两点的曼哈顿距离。所以对于每个询问看做一个点。我们要按一定顺序计算每个值。那开销就为曼哈顿距离的和。要计算到每个点。那么路径至少是一棵树。所以问题就变成了求二维平面的最小曼哈顿距离生成树。
#include<bits/stdc++.h>
#include<algorithm>
#include <math.h>
using namespace std;
#define ll long long
typedef pair<ll ,ll>pa;
#define pre(i,x,n) for(int i=x;i<=n;i++)
#define rep(i,n,x) for(int i=n;i>=x;i--)
//priority_queue<ll ,vector<ll>,greater<ll> >q;
ll n,m,a[50010],ans=0,cnt[50010]={0},sum[50010][3];
int tem;
struct mo
{
int l,r,id;
}q[50010];
int cmp(mo x,mo y)
{
if(x.l/tem==y.l/tem)
return x.r<y.r;
return x.l/tem<y.l/tem;
}
int gcd(ll x,ll y)
{
return x?gcd(y%x,x):y;
}
void add(int x)
{
ans-=cnt[a[x]]*cnt[a[x]];
cnt[a[x]]++;
ans+=cnt[a[x]]*cnt[a[x]];
}
void del(int x)
{
ans-=cnt[a[x]]*cnt[a[x]];
cnt[a[x]]--;
ans+=cnt[a[x]]*cnt[a[x]];
}
int main()
{
scanf("%lld%lld",&n,&m);
tem=sqrt(n);
pre(i,1,n)scanf("%lld",&a[i]);
pre(i,1,m)
{
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+1+m,cmp);
int l=1,r=0;
pre(i,1,m)
{
while(r<q[i].r)add(++r);
while(l>q[i].l)add(--l);
while(l<q[i].l)del(l++);
while(r>q[i].r)del(r--);
sum[q[i].id][0]=ans-r+l-1;
sum[q[i].id][1]=1ll*(r-l+1)*(r-l);
//一定要先判断一下,防止出现gcd(0,0)的情况,让后面g=0,提交时出现RE(运行错误)
if(q[i].l==q[i].r)
{
sum[q[i].id][0]=0;
sum[q[i].id][1]=1;
continue;
}
int g=gcd(sum[q[i].id][0],sum[q[i].id][1]);
sum[q[i].id][0]/=g;
sum[q[i].id][1]/=g;
}
pre(i,1,m){printf("%lld/%lld\n",sum[i][0],sum[i][1]);}
return 0;
}