P1494 [国家集训队] 小 Z 的袜子
P1494 [国家集训队] 小 Z 的袜子
题目描述
作为一个生活散漫的人,小 Z 每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小 Z 再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小 Z 把这
你的任务便是告诉小 Z,他有多大的概率抽到两只颜色相同的袜子。当然,小 Z 希望这个概率尽量高,所以他可能会询问多个
然而数据中有 0/1
。
提示
Solution :
十分简单的莫队,非常适合入门。
我们发现分母是很容易确定的,所以我们统计分子就好了。考虑用桶维护区间内的颜色出现次数
那么这个贡献显然是可以
至于莫队算法嘛:
我似乎不能用比较精炼的语言概括其思想,但是它能应用的特征十分明显:询问离线,无修改操作,插入或删除一个点时,重新统计贡献的复杂度不高。
算法流程:
我们将所有询问排序,用两个指针
Code:
#include<bits/stdc++.h> #define int long long const int N=5e4+5; using namespace std; int a[N],bac[N],blo[N],ans[N],len[N]; struct task{ int l,r,L,R,id; bool operator<(const task &t)const{ return l==t.l ? (l&1 ? R<t.R : t.R<R) :l < t.l; } }q[N]; int n,m,S,sum; inline void add(int x){sum+=bac[a[x]]++;} inline void del(int x){sum-=--bac[a[x]];} int gcd(int x,int y){return y ? gcd(y,x%y) : x;} void work() { cin>>n>>m; S=sqrt(n); for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); blo[i]=(i-1)/S+1; } for(int i=1,l,r;i<=m;i++) { scanf("%lld%lld",&l,&r); q[i]={blo[l],blo[r],l,r,i}; len[i]=r-l+1; } sort(q+1,q+1+m); int l=1,r=0; for(int i=1;i<=m;i++) { while(r<q[i].R)add(++r); while(l<q[i].L)del(l++); while(q[i].L<l)add(--l); while(q[i].R<r)del(r--); ans[q[i].id]=sum<<1; } for(int i=1;i<=m;i++) { len[i]*=len[i]-1; if(len[i]) { int d=gcd(ans[i],len[i]); printf("%lld/%lld\n",ans[i]/d,len[i]/d); } else { printf("0/1\n"); } } } #undef int int main() { //freopen("P1494_1.in","r",stdin);freopen("P1494.out","w",stdout); work(); return 0; }