P1494 小Z的袜子 莫队

题干

就是将$add$和$del$函数里的$ans$变化变成组合数嘛,

先预处理出$x$只相同袜子一共有$f[x] = 1+2+...+$$(x-1)$种组合,

要注意,由于$f[x]$是一直加到$x-1$,所以我们要$add,del$两个函数中都要关注这个问题:

  $add$函数要先更改$ans$数值再改$cnt$,$del$函数要先更改$cnt$再改$ans$数值

再就是直接套莫队板子了,板子还是注意给问题排序时要分块

当然小学数学老师教过我们,分数再最后要约分

上代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define ll long long
#define NUM 50010
using namespace std;
ll f[NUM];//预处理的组合数
int a[NUM],cnt[NUM];
int n,m;
struct wen{
    int l,r,num;
};
wen q[NUM];//存下每个问题
int blo;//每个块内的元素数量
bool cmp( wen x,wen y ){
    if( x.r/blo == y.r/blo ) return x.l < y.l;
    return x.r < y.r;
}
ll ans;
void add( int x ){
    ans += cnt[a[x]];
    cnt[a[x]]++;
}
void del( int x ){
    cnt[a[x]]--;
    ans -= cnt[a[x]];
}
struct da{
    ll zi,mu;//对于每个问题的答案的分子分母
};
da anss[NUM];
ll gcd( int x,int y ){
    if( x < y ) swap( x,y );
    if( y == 0 ) return x;
    return gcd( y,x%y );
}
int main(){
    cin >> n >> m;
    blo = sqrt(n);
    for( int i = 1;i <= n;i++ )
        f[i] = f[i-1] + i;
    for( int i = 1;i <= n;i++ )
        cin >> a[i];
    for( int i = 1;i <= m;i++ ){
        cin >> q[i].l >> q[i].r;
        q[i].num = i;
    }
    sort( q+1,q+m+1,cmp );
    int l = 1,r = 0,ql,qr;
    for( int i = 1;i <= m;i++ ){
        ql = q[i].l,qr = q[i].r;
        if( qr == ql ){ //如题干所言,特判
            anss[q[i].num].zi = 0;
            anss[q[i].num].mu = 1;
            continue;
        }
        while( l < ql ){
            del( l );
            l++;
        }
        while( r > qr ){
            del( r );
            r--;
        }
        while( l > ql ){
            l--;
            add( l );
        }
        while( r < qr ){
            r++;
            add( r );
        }
        ll mu = f[qr-ql],p = gcd( ans,mu );
        anss[q[i].num].zi = ans/p;
        anss[q[i].num].mu = mu/p;//约分
    }
    for( int i = 1;i <= m;i++ )
        cout << anss[i].zi << "/" << anss[i].mu << endl;
    return 0;
}

 

posted @ 2022-02-11 08:13  little_sheep_xiaoen  阅读(30)  评论(0编辑  收藏  举报