【bzoj2038】[2009国家集训队]小Z的袜子(hose) (莫队算法)

来源:互联网 发布:java unicode编码表 编辑:程序博客网 时间:2024/06/02 18:14

题目描述

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命……
具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。
你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

输入输出格式

输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。
包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例)

考虑区间[L,R]所求概率。
总可能数为(RL)(RL1)(第一次取R-L种,第二次R-L-1种)
对于取出两双的可能性,假设L,R中袜子的颜色分别为a,b,c,d……,而出现的个数为cnt[a],cnt[b],cnt[c],cnt[d]……
那么显然,ans=(cnt[a](cnt[a]1))+(cnt[b](cnt[b]1))++(cnt[](cnt[]1))(RL)(RL1)
那么化简后得
ans=cnt[a]2+cnt[b]2++cnt[]2(cnt[a]+cnt[b]++cnt[])(RL)(RL1)
=cnt[a]2+cnt[b]2++cnt[]2(RL+1)(RL)(RL1)

那么需要做的就是维护区间L,R中所有cnt[]^2,线段树感觉不可做。

那么此时就需要一个神奇的算法,莫队算法。
这个算法是由国家队的大佬发明的(%%%),可以处理一些离线而且可以由[L,R]O(1)得到[L+-1,R+-1]的询问。
首先我们考虑一下对于询问[L1,R1],[L2,R2],假如满足如上O(1)时间的限制,那么我们从[L1,R1]到[L2,R2]一共需要|L1-L2|+|R1-R2|次的操作。
即相当于两个点的曼哈顿距离。
对于三个询问
1,3
3,5
2,4
显然我们先处理第一个询问,再处理第三个,最后处理第二个的话总的曼哈顿距离是最小的。
莫队算法就是一种将询问离线并进行排序后再暴力的算法,时间复杂度却可以得到显著的提升。
这个排序可以怎么做呢,显然假如把所有询问看成点那么就是求二维平面最小曼哈顿距离生成树,但是代码的复杂度较高。
感兴趣可以看这个大佬的
而另一种简便的方式便是分块。
按照每个询问左端点与右端点所在的块进行排序,左端点为第一关键字,右端点为第二关键字。
至于复杂度分析,我就贴一下黄学长写的

1、i与i+1在同一块内,r单调递增,所以r是O(n)的。由于有n^0.5块,所以这一部分时间复杂度是n^1.5。
2、i与i+1跨越一块,r最多变化n,由于有n^0.5块,所以这一部分时间复杂度是n^1.5
3、i与i+1在同一块内时l变化不超过n^0.5,跨越一块也不会超过n^0.5,忽略*2。由于有m次询问(和n同级),所以时间复杂度是n^1.5
于是就是O(n^1.5)了

#include<bits/stdc++.h>#define fer(i,j,n) for(int i=j;i<=n;i++)#define far(i,j,n) for(int i=j;i>=n;i--)#define ll long long#define pa pair<int,int>const int maxn=50010;const int INF=1e9+7;using namespace std;/*----------------------------------------------------------------------------*/inline ll read(){    char ls;ll x=0,sng=1;    for(;ls<'0'||ls>'9';ls=getchar())if(ls=='-')sng=-1;    for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';    return x*sng;}/*----------------------------------------------------------------------------*/ll n,m,now;ll a[maxn],belong[maxn],cnt[maxn],ans[maxn],tot[maxn];struct kaga{    int l,r;    int id;    bool friend operator <(kaga a,kaga b)    {        if(belong[a.l]==belong[b.l])return a.r<=b.r;        else return a.l<b.l;     }}q[maxn];void update(int x,int d){    now-=cnt[x]*cnt[x];    cnt[x]+=d;    now+=cnt[x]*cnt[x];}int main(){    n=read();m=read();    memset(cnt,0,sizeof(cnt));    int block=sqrt(n);    fer(i,1,n)    belong[i]=(i-1)/block+1;    fer(i,1,n)a[i]=read();    fer(i,1,m)    {        q[i].l=read(),q[i].r=read();        q[i].id=i;    }    sort(q+1,q+m+1);    //fer(i,1,m)cout<<q[i].l<<" "<<q[i].r<<endl;    int nowl=1,nowr=0;    fer(i,1,m)    {        ll l=q[i].l,r=q[i].r;        ll id=q[i].id;        if(l==r)        {            ans[id]=0;            tot[id]=1;          }        fer(j,nowr+1,r)update(a[j],1);        far(j,nowr,r+1)update(a[j],-1);        fer(j,nowl,l-1)update(a[j],-1);        far(j,nowl-1,l)update(a[j],1);        nowl=l;nowr=r;        ans[id]=now-(r-l+1);        tot[id]=(r-l+1)*(r-l);        ll gcd=__gcd(ans[id],tot[id]);        ans[id]/=gcd;        tot[id]/=gcd;    }    fer(i,1,m)    printf("%lld/%lld\n",ans[i],tot[i]);}
阅读全文
0 0