zoukankan      html  css  js  c++  java
  • P1494 [国家集训队]小Z的袜子/莫队学习笔记(误

    P1494 [国家集训队]小Z的袜子

    题目描述

    作为一个生活散漫的人,小(Z)每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小(Z)再也无法忍受这恼人的找袜子过程,于是他决定听天由命……

    具体来说,小(Z)把这(N)只袜子从(1)(N)编号,然后从编号(L)(R)((L)尽管小(Z)并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。

    你的任务便是告诉小(Z),他有多大的概率抽到两只颜色相同的袜子。当然,小(Z)希望这个概率尽量高,所以他可能会询问多个((L,R))以方便自己选择。

    然而数据中有(L=R)的情况,请特判这种情况,输出(0/1)

    输入输出格式

    输入格式:

    输入文件第一行包含两个正整数(N)(M)(N)为袜子的数量,(M)为小(Z)所提的询问的数量。接下来一行包含(N)个正整数(C_i),其中(C_i)表示第(i)只袜子的颜色,相同的颜色用相同的数字表示。再接下来(M)行,每行两个正整数(L)(R)表示一个询问。

    输出格式:

    包含(M)行,对于每个询问在一行中输出分数(A/B)表示从该询问的区间([L,R])中随机抽出两只袜子颜色相同的概率。若该概率为(0)则输出(0/1),否则输出的(A/B)必须为最简分数。(详见样例)

    说明

    (30\%)的数据中 (N,M le 5000)

    (60\%)的数据中 (N,M le 25000)

    (100\%)的数据中 (N,M le 50000,1 le L < R le N,Ci le N)


    莫队的基本思想是对询问进行分块,保证询问的集合有一定的顺序,使答案状态改变次数在能接受的范围内。

    对于此题

    先以(l)为关键字排序,然后将询问分成(sqrt m)块,对块内以(r)排序

    对每个块暴力处理第一个询问,然后右移右区间,暴力移动左区间

    每次移动左区间不会超过(sqrt n),右区间移动之和基本只是整个序列,复杂度是对的(有胡扯的嫌疑

    对每个答案状态维护

    (sum C_{color[i]}^2) (color[i])表示当前区间颜色为(i)的位置的个数

    显然答案再除上总情况就可以了

    吐糟一下,分块类似算法和淀粉质写起来很不爽不知道为什么。。


    Code:

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #define ll long long
    const ll N=5e4+10;
    struct node{ll l,r,id;}ask[N];
    bool cmp1(node a,node b){return a.l<b.l;}
    bool cmp2(node a,node b){return a.r<b.r;}
    ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    ll anx[N],any[N],n,m,m_,c[N],clo[N],sum;
    ll cal(ll d)
    {
        return d*(d-1)/2;
    }
    void updata(ll pos,ll d)
    {
        sum-=cal(clo[c[pos]]);
        clo[c[pos]]+=d;
        sum+=cal(clo[c[pos]]);
    }
    void solve(ll l,ll r)
    {
        memset(clo,0,sizeof(clo));
        sum=0;
        std::sort(ask+l,ask+r+1,cmp2);
        for(ll j=ask[l].l;j<=ask[l].r;j++)
            updata(j,1);
        ll p=cal(ask[l].r+1-ask[l].l);
        ll d=gcd(sum,p);
        anx[ask[l].id]=sum/d,any[ask[l].id]=p/d;
        for(ll j=l+1;j<=r;j++)
        {
            for(ll k=ask[j-1].r+1;k<=ask[j].r;k++)
                updata(k,1);
            if(ask[j-1].l<ask[j].l)
            {
                for(ll k=ask[j-1].l;k<ask[j].l;k++)
                    updata(k,-1);
            }
            else
            {
                for(ll k=ask[j-1].l-1;k>=ask[j].l;k--)
                    updata(k,1);
            }
            p=cal(ask[j].r+1-ask[j].l);
            d=gcd(sum,p);
            anx[ask[j].id]=sum/d,any[ask[j].id]=p/d;
        }
    }
    int main()
    {
        //freopen("data.in","r",stdin);
        //freopen("data.out","w",stdout);
        scanf("%lld%lld",&n,&m_);
        for(ll i=1;i<=n;i++) scanf("%lld",c+i);
        for(ll l,r,i=1;i<=m_;i++)
        {
            scanf("%lld%lld",&l,&r);
            if(l==r) anx[i]=0,any[i]=1;
            else ask[++m]={l,r,i};
        }
        std::sort(ask+1,ask+1+m,cmp1);
        ll t=sqrt(m)+1,k=1;
        for(;k*t<=m;k++)
        {
            ll l=(k-1)*t+1,r=k*t;
            solve(l,r);
        }
        --k;
        if(k*t!=m)
            solve(k*t+1,m);
        for(ll i=1;i<=m_;i++)
            printf("%lld/%lld
    ",anx[i],any[i]);
        return 0;
    }
    
    

    2018.9.29

  • 相关阅读:
    聊聊 Java8 以后各个版本的新特性
    如何使用SpringBoot封装自己的Starter
    Git原理入门解析
    Linux磁盘管理:LVM逻辑卷的拉伸及缩减
    LVM在线扩容
    Ubuntu setup Static IP Address
    ubuntu修改主机名
    user.sh
    升级Dell的R810固件版本
    DSET收集ESXi硬件日志
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9726744.html
Copyright © 2011-2022 走看看