[BZOJ 2038][2009国家集训队]小Z的袜子(hose)(莫队)
Description
作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。
Solution
把询问按照左端点排序,块内按照右端点排序;
(l,r)的答案转移到(l+1,r)/(l-1,r)/(l,r+1)/(l,r-1)只需要O(1)的时间,所以总的时间复杂度是O(n^1.5)【黄学长blog有时间复杂度的分析 hzwer】
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #define MAXN 50005 typedef long long LL; using namespace std; int n,m,unit,col[MAXN],num[MAXN]; LL ans=0; struct Node{ int l,r,id; LL a,b; }q[MAXN]; int Read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0';c=getchar(); } return x*f; } bool cmp(Node A,Node B) { if(A.l/unit!=B.l/unit) return A.l/unit<B.l/unit; return A.r<B.r; } bool cmp_id(Node A,Node B) { return A.id<B.id; } LL gcd(LL a,LL b) { if(!b)return a; return gcd(b,a%b); } void update(int x,int add) { ans-=num[col[x]]*(num[col[x]]-1); num[col[x]]+=add; ans+=num[col[x]]*(num[col[x]]-1); } void work() { int L=1,R=0; for(int i=0;i<m;i++) { while(q[i].l<L)update(L-1,1),L--; while(q[i].l>L)update(L,-1),L++; while(q[i].r<R)update(R,-1),R--; while(q[i].r>R)update(R+1,1),R++; if(q[i].l==q[i].r){q[i].a=0,q[i].b=1;continue;} q[i].a=ans; q[i].b=(LL)(R-L+1)*(R-L); LL k=gcd(q[i].a,q[i].b); q[i].a/=k,q[i].b/=k; } } int main() { n=Read();m=Read(); unit=sqrt(n); for(int i=0;i<n;i++) col[i]=Read(); for(int i=0;i<m;i++) { q[i].l=Read()-1; q[i].r=Read()-1; q[i].id=i; } sort(q,q+m,cmp); work(); sort(q,q+m,cmp_id); for(int i=0;i<m;i++) { printf("%lld/%lld\n",q[i].a,q[i].b); } return 0; }