bzoj2038 [2009国家集训队]小Z的袜子(hose) (莫队裸题)

83 篇文章 0 订阅

bzoj2038 [2009国家集训队]小Z的袜子(hose)

原题地址http://www.lydsy.com/JudgeOnline/problem.php?id=2038

题意:
小Z把N只袜子从1到N编号,每只袜子有颜色C,然后从编号L到R中任意抽取两只袜子,你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问M个(L,R)以方便自己选择。

数据范围
N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

题解:
(我的第一道莫队)
莫队裸题。
要开long long。
初始化如果l,r都设成0,要注意cnt[0]设为1,或者l=1,r=0。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#define LL long long
using namespace std;
const int N=50010;
int n,m,s,col[N],cnt[N];
LL sum,ans[N][2];
int bel(int x)
{
    return (x-1)/s+1;
}
struct node
{
    int L,R,note;
    node(){}
    node(int L,int R,int note) : L(L),R(R),note(note) {}
}q[N];
bool cmp(const node &A,const node &B)
{
    if(bel(A.L)!=bel(B.L)) return A.L<B.L;
    else return A.R<B.R;
}
void modify(int x,int val)
{
    sum-=1LL*cnt[col[x]]*1LL*(cnt[col[x]]-1);
    cnt[col[x]]+=val;
    sum+=1LL*cnt[col[x]]*1LL*(cnt[col[x]]-1);
}
LL gcd(LL x,LL y)
{
    return (y==0)?x:gcd(y,x%y);
} 
void simp(LL x)
{
    if(ans[x][0]==0) {ans[x][1]=1;return;}
    LL g=gcd(ans[x][0],ans[x][1]);
    ans[x][0]/=g; ans[x][1]/=g;
    return;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    scanf("%d",&col[i]);
    s=sqrt(n);
    for(int i=1;i<=m;i++)
    {
        int L,R;
        scanf("%d%d",&L,&R);
        q[i]=node(L,R,i);
    }
    sort(q+1,q+m+1,cmp);
    int lf=1; int rg=0;  //!!!
    sum=0;
    for(int i=1;i<=m;i++)
    {
        int L=q[i].L; int R=q[i].R; int note=q[i].note;
        while(rg<R) modify(++rg,1);
        while(rg>R) modify(rg--,-1);
        while(lf>L) modify(--lf,1);
        while(lf<L) modify(lf++,-1);
        ans[note][0]=1LL*sum;
        ans[note][1]=1LL*(R-L+1)*1LL*(R-L);
    }
    for(int i=1;i<=m;i++)
    {
        simp(i);
        printf("%I64d/%I64d\n",ans[i][0],ans[i][1]);
    }

    return 0;
}

引用下关于莫队的复杂度证明

假设我们每k个点分一块。

如果当前询问与上一询问左端点处在同一块,那么左端点移动为O(k)。虽然右端点移动可能高达O(n),但是整一块询问的右端点移动距离之和也是O(n)(想一想,为什么)。因此平摊意义下,整块移动为O(k) × O(k) + O(n),一共有n / k块,时间复杂度为O(kn + n2 / k)。

总的移动次数为O(kn + n2 / k)。因此,当k = √n时,运行时间上界最优,为O( n1.5 )。

最后,因此根据每次insert和erase的时间复杂度,乘上O(1)或者O(logn)亦或O(n)不等,得到完整算法的时间复杂度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值