[国家集训队]小Z的袜子

题目大意

莫队入门题

序列中有n个数

每次询问在区间[l,r]中,随机取两个数,数值相同的概率

解题思路

既然是莫队入门题,当然要用莫队做

常规操作:对询问排序!

排序就不多说,重点在指针移动时对答案的贡献

首先,令cnt[i]表示区间[l,r]中数值为i的个数

那么我们很容易推出公式:

\[ ans=\frac{\sum_{i=0}^{n}C(cnt_i,2)}{C(r-l+1,2)} \]

设增加一个颜色P(右指针右移一格或左指针左移一格),

那么分子变化量

\begin{align} \Delta &=C(cnt_P+1,2)-C(cnt_P,2) \\ &=cnt_P \end{align}

分母变化量

\begin{align} \Delta &=C(r-l+2,2)-C(r-l+1,2) \\ &=r-l+1 \end{align}

于是指针移动时可以\(O(1)\)转移分子和分母

#include<iostream>
#include<cstdio>
#include<algorithm>

int n,m;

struct node{
    int col,inK;
}T[100000];

struct query{
    int l,r;
    int id;
    bool operator < (query P){return T[P.l].inK<T[P.r].inK||T[P.l].inK==T[P.r].inK&&P.l<P.r;}
}Q[100000];

int sqrt(long long k){
    long long l=0,r=k,mdl;
    while (l<r){
        mdl=(l+r+1)>>1;
        if (mdl*mdl<=k) l=mdl;
        else r=mdl-1;
    }
    return l;
}

long long gcd(long long a,long long b){return (!b)?a:(gcd(b,a%b));}

int blo;

int cnt[100000],x,y,l=1,r=0;

std::pair<int,int> ans[100000];

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) scanf("%d",&T[i].col);
    blo=sqrt(n);
    for (int i=1;i<=blo;i++){
        int L=n*(i-1)/blo+1,R=n*i/blo;
        for (int j=L;j<=R;j++) T[j].inK=i;
    }
    for (int i=1;i<=m;i++){
        scanf("%d%d",&Q[i].l,&Q[i].r);
        Q[i].id=i;
    }
    std::sort(Q+1,Q+m+1);
    for (int i=1;i<=m;i++){
        while (l<Q[i].l){
            cnt[T[l].col]--;
            x-=cnt[T[l].col];
            y-=(r-l);
            l++;
        }
        while (r>Q[i].r){
            cnt[T[r].col]--;
            x-=cnt[T[r].col];
            y-=(r-l);
            r--;
        }
        while (l>Q[i].l){
            l--;
            x+=cnt[T[l].col];
            y+=r-l;
            cnt[T[l].col]++;
        }
        while (r<Q[i].r){
            r++;
            x+=cnt[T[r].col];
            y+=r-l;
            cnt[T[r].col]++;
        }
        if (l==r) ans[Q[i].id]=std::make_pair(0,1);
        else{
            int d=gcd(x,y);
            ans[Q[i].id]=std::make_pair(x/d,y/d);
        }
    }
    for (int i=1;i<=m;i++) printf("%d/%d\n",ans[i].first,ans[i].second);
}

猜你喜欢

转载自www.cnblogs.com/ytxytx/p/9458525.html
今日推荐