P1494 [国家集训队]小Z的袜子(莫队)

时间:2023-03-08 18:53:52
P1494 [国家集训队]小Z的袜子(莫队)

题目链接:https://www.luogu.org/problemnew/show/P1494

题目大意:中文题目

具体思路:计算概率的时候,每一次是区间的移动,每一次移动,记得先将原来的记录的影响去掉,然后再加上新的值,每一次这样统计就可以了。然后计算概率的时候就是C(n,2),总的概率的计算方法,当前的颜色是的个数是t,然后就是C(t,2)。总的概率就是C(r-l+1,2),这里的r-l+1指的是当前要求的区间的长度。所以,合法的区间就是C(t,2)/C(r-l+1,2).

注意特判l==r的情况。

注意在减去原来的值的影响的时候,不能特判当前的值是不是大于2的,打个比方,原来的当前的值出现的是两次,我们先减去1,然后当前的值变成了1,这个时候我们按道理是需要将原来的影响给删掉,如果特判是不是大于等于2的话,就不可能减去了。

AC代码:

 #include<iostream>
#include<stack>
#include<cstring>
#include<iomanip>
#include<stdio.h>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
# define ll long long
const int maxn = 2e5+;
ll a[maxn],vis[maxn];
struct node{
ll l,r,pos,id;
bool friend operator < (node t1,node t2){
if(t1.pos==t2.pos)return t1.r<t2.r;
return t1.pos<t2.pos;
}
}q[maxn];
struct qq{
ll t1,t2;
}ans[maxn];
int main(){
// freopen("hqx.in","r",stdin);
ll n,m;
scanf("%lld %lld",&n,&m);
ll block=(ll)sqrt(n);
for(ll i=;i<=n;i++){
scanf("%lld",&a[i]);
}
for(ll i=;i<=m;i++){
scanf("%lld %lld",&q[i].l,&q[i].r);
q[i].id=i;
q[i].pos=(q[i].l-)/block+;
}
sort(q+,q+m+);
ll tmp=;
ll l=,r=;
for(ll i=;i<=m;i++){
while(l<q[i].l){vis[a[l]]--;
tmp-=(vis[a[l]]+)*vis[a[l]];
tmp+=vis[a[l]]*(vis[a[l]]-);
l++;
}
while(l>q[i].l){l--;vis[a[l]]++;
tmp-=(vis[a[l]]-)*(vis[a[l]]-);
tmp+=vis[a[l]]*(vis[a[l]]-);
}
while(r<q[i].r){
r++;vis[a[r]]++;
tmp-=(vis[a[r]]-)*(vis[a[r]]-);
tmp+=vis[a[r]]*(vis[a[r]]-);
}
while(r>q[i].r){
vis[a[r]]--;
tmp-=(vis[a[r]]+)*(vis[a[r]]);
tmp+=(vis[a[r]]*(vis[a[r]]-));
r--;
}
if(q[i].l==q[i].r){
ans[q[i].id].t1=;
ans[q[i].id].t2=;
continue;
}
ans[q[i].id].t1=tmp;
ans[q[i].id].t2=(q[i].r-q[i].l+)*(q[i].r-q[i].l);
}
for(ll i=;i<=m;i++){
ll tmp=__gcd(ans[i].t1,ans[i].t2);
if(ans[i].t1/tmp>ans[i].t2/tmp)printf("1/1\n");
else
printf("%lld/%lld\n",ans[i].t1/tmp,ans[i].t2/tmp);
}
return ;
}