zoukankan      html  css  js  c++  java
  • BZOJ2038小Z的袜子(hose)

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

    Time Limit: 20 Sec  Memory Limit: 259 MB
    Submit: 2343  Solved: 1077
    [Submit][Status]

    Description

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

    Input

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

    Output

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

    Sample Input

    6 4
    1 2 3 3 3 2
    2 6
    1 3
    3 5
    1 6

    Sample Output

    2/5
    0/1
    1/1
    4/15
    【样例解释】
    询问1:共C(5,2)=10种可能,其中抽出两个2有1种可能,抽出两个3有3种可能,概率为(1+3)/10=4/10=2/5。
    询问2:共C(3,2)=3种可能,无法抽到颜色相同的袜子,概率为0/3=0/1。
    询问3:共C(3,2)=3种可能,均为抽出两个3,概率为3/3=1/1。
    注:上述C(a, b)表示组合数,组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。
    【数据规模和约定】
    30%的数据中 N,M ≤ 5000;
    60%的数据中 N,M ≤ 25000;
    100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。

    HINT

     

    Source

    题解:
    这个问题有个特殊的性质:就是知道了[l,r]的ans后可以在O(1)时间推出相邻点(l+1,r),(l-1,r),(l,r+1),(l,r-1)的ans
    所以可以用莫队算法
    附上ydc君对时间复杂度的证明以及讲解:
    --------------------------------------------------------------------------------------------------------------------------------------------

    能使用莫队算法的前提是这样的
    如果我们已知[l,r]的答案,能在O(1)时间得到[l+1,r]的答案以及[l,r-1]的答案,即可使用莫队算法。时间复杂度为O(n^1.5)。如果那个只能在logn的时间求,则时间复杂度是O(n^1.5*log n)。
    说白了,就是用一个“神奇的数据结构”维护插入、删除操作

    这道题的话我们很容易用“数组”来实现那个“神奇的数据结构”,做到O(1)的从[l,r]转移到[l,r+1]与[l+1,r]

    那么莫队算法怎么做呢?以下都是在转移为O(1)的基础下讨论的时间复杂度。另外由于n与m同阶,为了书写方便,我就全部写成n了……
    如果已知[l,r]的答案,要求[l',r']的答案,我们很容易在O( | l - l' + | r - r' | )的时间复杂度内求得

    莫涛大神是这么说的:把询问[l,r]抽象成一个点(l,r),题目就转化为求n个点的最小曼哈顿哈密尔顿路;由于这是个NP问题,所以我们希望找到一个稍微劣一点又不是很劣的但是能快速求得方案。莫涛大神在他的论文里使用了二维曼哈顿距离最小生成树
    二维曼哈顿距离最小生成树可以用区域划分法+树状数组/线段树维护区间极值在nlogn的时间复杂度内完成构图,并用Kruskal在n log n时间内求得,但是代码比较繁琐。
    这里介绍一个优美的替代品——分块
    将n个数分成sqrt(n)块
    按区间排序,以左端点所在块内为第一关键字,右端点为第二关键字,进行排序
    也就是以( pos [l],r )排序
    然后搞就可以了

    为什么呢?
    搞得过程是这样的:
    一、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)了

    -------------------------------------------------------------------------------------------------------------------------------------------

    代码:

     1 type node=record
     2      l,r,id,x,y:int64;
     3      end;
     4 var a,ans:array[1..55000] of node;
     5     s,c,p:array[1..55000] of int64;
     6     n,m,block,l,r,k:longint;
     7     i:longint;
     8     anss:int64;
     9 function gcd(x,y:int64):int64;
    10  begin
    11  if y=0 then exit(x) else exit(gcd(y,x mod y));
    12  end;
    13 function cmp(x,y:node):boolean;
    14  begin
    15  if p[x.l]=p[y.l] then exit(x.r<y.r) else exit(x.l<y.l);
    16  end;
    17 procedure sort(h,l:longint);
    18  var i,j:longint;
    19      tmp,mm:node;
    20  begin
    21   i:=h;j:=l;mm:=a[(i+j)>>1];
    22   repeat
    23     while cmp(a[i],mm) do inc(i);
    24     while cmp(mm,a[j]) do dec(j);
    25     if i<=j then
    26       begin
    27        tmp:=a[i];a[i]:=a[j];a[j]:=tmp;
    28        inc(i);dec(j);
    29       end;
    30   until i>j ;
    31   if i<l then sort(i,l);
    32   if j>h then sort(h,j);
    33  end;
    34 procedure init;
    35  begin
    36    readln(n,m);
    37    for i:=1 to n do read(c[i]);
    38    block:=trunc(sqrt(n));
    39    for i:=1 to n do p[i]:=(i-1) div block+1;
    40    for i:=1 to m do
    41     begin
    42       readln(a[i].l,a[i].r);
    43       a[i].id:=i;
    44     end;
    45  end;
    46 procedure update(p,add:longint);
    47  begin
    48   dec(anss,sqr(s[c[p]]));
    49   inc(s[c[p]],add);
    50   inc(anss,sqr(s[c[p]]));
    51  end;
    52 procedure main;
    53  begin
    54   fillchar(s,sizeof(s),0);
    55   sort(1,m);
    56   anss:=0;
    57   l:=1;r:=0;
    58   for i:=1 to m do
    59     begin
    60       while r<a[i].r do
    61         begin
    62          update(r+1,1);inc(r);
    63         end;
    64       while r>a[i].r do
    65         begin
    66          update(r,-1);dec(r);
    67         end;
    68       while l<a[i].l do
    69         begin
    70         update(l,-1);inc(l);
    71         end;
    72       while l>a[i].l do
    73         begin
    74         update(l-1,1);dec(l);
    75         end;
    76       if a[i].l=a[i].r then
    77         begin
    78          a[i].x:=0;a[i].y:=1;
    79          continue;
    80         end;
    81       with a[i] do
    82        begin
    83          x:=anss-(r-l+1);
    84          y:=(r-l+1)*(r-l);
    85          k:=gcd(x,y);
    86          x:=x div k;y:=y div k;
    87        end;
    88       end;
    89   for i:=1 to m do ans[a[i].id]:=a[i];
    90   for i:=1 to m do with ans[i] do writeln(x,'/',y);
    91   end;
    92 begin
    93   assign(input,'input.txt');assign(output,'output.txt');
    94   reset(input);rewrite(output);
    95   init;
    96   main;
    97   close(input);close(output);
    98 end.        
    View Code

    (ps:如果调试的时候确认自己的算法没错,那就一定是数据范围出错了!)

     
     
  • 相关阅读:
    从三道题目入门frida
    APP 抓包(应用层)
    安卓开发--探究碎片Fragment
    centos7-查询内存/硬盘等详细信息
    网页视频下载神器
    error: C++ preprocessor "/lib/cpp" fails sanity check错误解决方法
    make源文件时出现 /usr/bin/ld: cannot find -lstdc++ 错误
    安装docker后修改docker文件目录
    centos集群中各节点如何实现时间同步?
    redhat7.5 yum不能用
  • 原文地址:https://www.cnblogs.com/zyfzyf/p/3896199.html
Copyright © 2011-2022 走看看