您的位置:首页 > 其它

bzoj 2038 [2009国家集训队]小Z的袜子(hose)(莫队算法)

2016-04-01 09:07 531 查看
【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=2038

【题意】

给定一个有颜色的序列,回答若干个询问:区间内任选两个颜色相同的概率。

【思路】

设一个颜色在区间内的出现次数为cnt,则抽到这种颜色的概率为:

(cnt-1)*cnt/2 = 1+2+…+cnt-1

对于一个区间我们就可以使用一个cnt数组通过扫一遍得出答案。暴力的话,可以拿两个指针,每次移动指针,对数据进行插入与删除。

莫队算法就是在此基础上有序化了数据。将序列每sqrt(n)分成一块,根据查询的左端点所在块编号和右端点为第一二关键字排序。相邻的区间的重叠部分不重复计算,而是采用移动指针的方法,并通过合适的数据结构维护区间内的数据,支持在原来答案的基础上添加或删除数据即区间移动。

关于复杂度的分析:


一、i与i+1在同一块内,r单调递增,所以r是O(n)的。由于有n^0.5块,所以这一部分时间复杂度是n^1.5。
二、i与i+1跨越一块,r最多变化n,由于有n^0.5块,所以这一部分时间复杂度是n^1.5
三、i与i+1在同一块内时变化不超过n^0.5,跨越一块也不会超过2*n^0.5,不妨看作是n^0.5。由于有n个数,所以时间复杂度是n^1.5
于是就变成了O(n^1.5)了

                                          from...不详



【代码】

#include<set>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define trav(u,i) for(int i=front[u];i;i=e[i].nxt)
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std;

typedef long long ll;
const int N = 5e4+10;

ll read() {
char c=getchar();
ll f=1,x=0;
while(!isdigit(c)) {
if(c=='-') f=-1; c=getchar();
}
while(isdigit(c))
x=x*10+c-'0',c=getchar();
return x*f;
}

struct Node
{
int pos,l,r,id;
bool operator < (const Node& rhs) const
{
return pos<rhs.pos||(pos==rhs.pos&&r<rhs.r);
}
}q
;

ll gcd(ll a,ll b)
{
return b==0? a:gcd(b,a%b);
}

int n,m,c
,cnt
;
pair<int,int> ans
;

int main()
{
n=read(),m=read();
FOR(i,1,n) c[i]=read();
int B=sqrt(n);
FOR(i,1,m)
{
q[i].l=read();
q[i].r=read();
q[i].pos=(q[i].l-1)/B+1;
q[i].id=i;
}
sort(q+1,q+m+1);
int l=1,r=0; ll now=0;
FOR(i,1,m)
{
while(l>q[i].l)
now+=cnt[c[--l]]++;
while(l<q[i].l)
now-=--cnt[c[l++]];
while(r<q[i].r)
now+=cnt[c[++r]]++;
while(r>q[i].r)
now-=--cnt[c[r--]];
ll sum=(ll)(r-l+1)*(r-l)/2;
ll g=gcd(sum,now);
ans[q[i].id]=make_pair((int)now/g,(int)sum/g);
}
FOR(i,1,m)
printf("%d/%d\n",ans[i].first,ans[i].second);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: