正题
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1494
题目大意
区间任意取两个数,求取到相同的数的概率。
解题思路
假设一个区间有x个y,那么两个都取到y的方案数是x∗(x−1)x*(x-1)x∗(x−1),那么取到相同总共方案数∑i=1nxi∗(xi−1)\sum_{i=1}^nx_i*(x_i-1)∑i=1nxi∗(xi−1)。总共取的方案数(r−l+1)∗(r−l)(r-l+1)*(r-l)(r−l+1)∗(r−l),答案就是
∑i=1nxi∗(xi−1)(r−l+1)∗(r−l)\frac{\sum_{i=1}^nx_i*(x_i-1)}{(r-l+1)*(r-l)}(r−l+1)∗(r−l)∑i=1nxi∗(xi−1)
对于每个xix_ixi,直接莫队就好了。
不过莫队要用分块优化一下
code
#include<cstdio>
#include<cmath>
#include<algorithm>
#define N 50010
#define ll long long
using namespace std;
struct node{ll l,r,id;
}a[N];
struct ans_node{ll x,y;
}ans[N];
ll n,m,w[N],l,r,now,id[N],t;
ll cnt[N];
inline bool cmp(node x,node y){if (id[x.l]==id[y.l]){if (id[x.l]&1==1) return x.r<y.r;else return x.r>y.r; }else return id[x.l]<id[y.l];
}//分块优化莫队——排序
inline void add(ll x)//加一个数
{cnt[x]++;if(cnt[x]>1)now+=cnt[x]*(cnt[x]-1)-(cnt[x]-1)*(cnt[x]-2);
}
inline void del(ll x)//去掉一个数
{cnt[x]--;if(cnt[x]>0)now+=cnt[x]*(cnt[x]-1)-(cnt[x]+1)*cnt[x];
}
void get_ans(ll x,ll y,ll id)//计算答案
{if(!x)y=1;else{ll d=__gcd(x,y);x/=d;y/=d;}ans[id].x=x;ans[id].y=y;
}
int main()
{freopen("testdata.in","r",stdin);freopen("data.out","w",stdout);scanf("%lld%lld",&n,&m);t=sqrt(n);for(ll i=1;i<=n;i++)scanf("%lld",&w[i]),id[i]=(i-1)/t+1;for(ll i=1;i<=m;i++)scanf("%lld%lld",&a[i].l,&a[i].r),a[i].id=i;sort(a+1,a+1+m,cmp);l=a[1].l;r=a[1].r;for(ll i=l;i<=r;i++)add(w[i]);get_ans(now,(r-l+1)*(r-l),a[1].id);for(ll i=2;i<=m;i++){while(l<a[i].l) del(w[l++]);while(l>a[i].l) add(w[--l]);while(r<a[i].r) add(w[++r]);while(r>a[i].r) del(w[r--]);//边框移动get_ans(now,(r-l+1)*(r-l),a[i].id);}for(ll i=1;i<=m;i++)printf("%lld/%lld\n",ans[i].x,ans[i].y);
}