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; }