这是一道很好也很烦的综合题……

首先我们肯定要先把f(i)处理出来这是毫无疑问的

我们要求出数位乘积为now的个数,首先是空间上的问题

直接肯定会爆空间,不难发现

乘积的质因数只有2,3,5,7,并且指数也不是特别大

暴力可得到不同的乘积最多只有15000不到

然后我们就可以对其离散化然后数位dp

dp完之后,对于点(p,q),这上面的金子个数是sum(p)*sum(q)个 (sum表示数位乘积为p的数的个数)

然后我们要求金子前k多的点,当状态太多无法算出所有状态求最优值时,我们常常用堆来维护

首先我们对sum排序(假定降序),对于每个乘积x,设乘积x离散化后对应的编号为w(x)

在x行上金子最多的点一定是sum[w(x)]*sum[1],次大的点一定是sum[w(x)]*sum[2]……

然后我们对当前这n行上最多的点维护一个大根堆

当我们每次将堆顶的点取出时,堆顶所在行的下一大的点可能是之后选取结果产生影响,

因此我们要将这行下一大的点加入堆,一共只要弹k次,所以复杂度为O(klogt)

 const maxn=;
      mo=; type arr=array[..maxn] of int64; var a,sum,h,loc,num:arr;
    b:array[..] of int64;
    f:array[..,..maxn] of int64;
    n,ans,x:int64;
    c,m,t,i:longint; procedure swap(var a,b:int64);
  var c:int64;
  begin
    c:=a;
    a:=b;
    b:=c;
  end; procedure sort(l,r:longint;var a:arr);
  var i,j:longint;x,y:int64;
  begin
    i:=l;
    j:=r;
    x:=a[(l+r) shr ];
    repeat
      while a[i]<x do inc(i);
      while x<a[j] do dec(j);
      if i<=j then
      begin
        swap(a[i],a[j]);
        inc(i);
        dec(j);
      end;
    until i>j;
    if i<r then sort(i,r,a);
    if j>l then sort(l,j,a);
  end; procedure prepare;
  var i,j,k,l:int64;
  begin
    i:=;
    while i<=n do
    begin
      j:=i;
      while j<=n do
      begin
        k:=j;
        while k<=n do
        begin
          l:=k;
          while l<=n do
          begin
            inc(m);
            a[m]:=l;
            l:=l*;
          end;
          k:=k*;
        end;
        j:=j*;
      end;
      i:=i*;
    end;
    sort(,m,a);
  end; procedure work;
  begin
    t:=;
    x:=n;
    while x<> do
    begin
      inc(t);
      b[t]:=x mod ;
      x:=x div ;
    end;
  end; function find(l,r:longint;x:int64):longint;
  var mid:longint;
  begin
    while l<r do
    begin
      mid:=(l+r)shr ;
      if a[mid]=x then exit(mid);
      if a[mid]<x then l:=mid+ else r:=mid-;
    end;
    if a[l]=x then exit(l);
    exit();
  end; procedure count;
  var i,j,k:longint;
      now,y:int64;
  begin
    work;
    f[][]:=;  //f[i,j]表示到第i位,乘积为j(j是离散化后的排名)的方案数
    for i:= to t- do
      for j:= to m do
        for k:= to do
          if (a[j] mod k=) then
            f[i,j]:=f[i,j]+f[i-,find(,j,a[j] div k)];     now:=;
    for i:=t downto do
    begin
      for j:= to b[i]- do  //肯定不可能是0
        for k:= to m do
          if (a[k]>=now*j) and ((a[k] div now) mod j=) and (a[k] mod now=) then
            sum[k]:=sum[k]+f[i-,find(,k,a[k] div (now*j))];
      now:=now*b[i];
      if now= then break;
    end;
    if now<> then inc(sum[find(,m,now)]);
    for i:= to m do
      for j:= to t- do
        sum[i]:=sum[i]+f[j,i];
  end; procedure sift(i,n:longint);
  var j:longint;
      x:int64;
  begin
    x:=h[i];
    j:=i shl ;
    while j<=n do
    begin
      if (j<n) and (h[j]<h[j+]) then inc(j);
      if x>=h[j] then exit
      else begin
        swap(h[i],h[j]);
        swap(loc[i],loc[j]);
        swap(num[i],num[j]);
        i:=j;
        j:=j shl ;
      end;
    end;
  end; begin
  readln(n,c);
  prepare;
  work;
  count;
  sort(,m,sum);  //为了统一形式,这里是升序
  for i:= to m do
  begin
    h[i]:=sum[i]*sum[m];  //当前第i行上最大的点
    loc[i]:=m;  
    num[i]:=i;  //所代表的行
  end;
  for i:=m downto do
    sift(i,m);
  ans:=;
  for i:= to c do
  begin
    ans:=(ans+(h[] mod mo)) mod mo;
    dec(loc[]);  //加入这行下一大的点
    h[]:=sum[num[]]*sum[loc[]];
    sift(,m);
  end;
  writeln(ans);
end.

bzoj3131的更多相关文章

  1. [BZOJ3131] [Sdoi2013]淘金

    [BZOJ3131] [Sdoi2013]淘金 Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐 ...

  2. bzoj千题计划268:bzoj3131: [Sdoi2013]淘金

    http://www.lydsy.com/JudgeOnline/problem.php?id=3131 如果已知 s[i]=j 表示有j个<=n数的数码乘积=i 那么就会有 s[a1]*s[a ...

  3. [您有新的未分配科技点]数位DP:从板子到基础(例题 bzoj1026 windy数 bzoj3131 淘金)

    只会统计数位个数或者某种”符合简单规律”的数并不够……我们需要更多的套路和应用 数位dp中常用的思想是“分类讨论”思想.下面我们就看一道典型的分类讨论例题 1026: [SCOI2009]windy数 ...

  4. 【数位dp】bzoj3131: [Sdoi2013]淘金

    思路比较自然,但我要是考场上写估计会写挂:好像被什么不得了的细节苟住了?…… Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1. ...

  5. [Bzoj3131][Sdoi2013]淘金(数位dp)(优先队列)

    3131: [Sdoi2013]淘金 Time Limit: 30 Sec  Memory Limit: 256 MBSubmit: 847  Solved: 423[Submit][Status][ ...

  6. [bzoj3131]淘金[sdoi2013][数位DP]

    求出每个数i可以被转移到的数目$f[i]$,则点$(i,j)$中的金子数目为$f[i]*f[j]$,我们就可以用优先队列求解前$k$大. 首先所有的积数目在$10^4$左右,可以先Dfs搜索出所有的数 ...

  7. bzoj2757

    非常神的数位dp,我调了几乎一天首先和bzoj3131类似,乘积是可以预处理出来的,注意这里乘积有一个表示的技巧因为这里质因数只有2,3,5,7,所以我们可以表示成2^a*3^b*5^c*7^d,也就 ...

  8. 【Richard 的刷(水)题记录】

    大概想了想,还是有个记录比较好. 9/24 网络流一日游: 最大流:bzoj1711[Usaco2007 Open]Dining 拆点 BZOJ 3993 Sdoi2015 星际战争 二分 P.S.这 ...

  9. OI动态规划&&优化 简单学习笔记

    持续更新!! DP的难点主要分为两类,一类以状态设计为难点,一类以转移的优化为难点. DP的类型 序列DP [例题]BZOJ2298 problem a 数位DP 常用来统计或者查找一个区间满足条件的 ...

随机推荐

  1. SqlServer Change Data Capture(CDC)数据变更捕获

    最近在使用SqlServer2008r2数据库做系统的时候,在某些重要的.经常涉及到修改的表上,想加上一些恢复机制,一开始想找找看看有没有类似Oracle数据库闪回那样的功能,后来发现CDC的功能可以 ...

  2. WPF-TxtBox控件利用KeyDown来控制键盘输入

    private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)        {            TextBox txt = ...

  3. Android开发MVP模式解析

    http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html 在开发Android应用时,相信很多同学遇到和我一样的情况,虽然 ...

  4. Effective C++ 学习总结

    摒弃C的做法采用C++的实现方式 以const和inline代替define 以iostream流代替stdio 以new和delete代替 C++风格注释 内存管理 new和delete, new[ ...

  5. C++多态性的理解

    本文章转载来自:http://www.sollyu.com/?p=627 代码 #include <iostream.h> class Animal { public: void eat( ...

  6. Shell中特殊符号

    http://blog.chinaunix.net/u1/53027/showart.php?id=482234 在shell中常用的特殊符号罗列如下:# ;    ;;       .       ...

  7. MyBatis 拦截器 (实现分页功能)

    由于业务关系 巴拉巴拉巴拉 好吧 简单来说就是 原来的业务是 需要再实现类里写 selectCount 和selectPage两个方法才能实现分页功能 现在想要达到效果是 只通过一个方法就可以实现 也 ...

  8. 【转】C#窗体飞入飞出的动画效果(Api)

    [System.Runtime.InteropServices.DllImport("user32")] private static extern bool AnimateWin ...

  9. 让你的 Node.js 应用跑得更快的 10 个技巧

    Node.js 受益于它的事件驱动和异步的特征,已经很快了.但是,在现代网络中只是快是不行的.如果你打算用 Node.js 开发你的下一个Web 应用的话,那么你就应该无所不用其极,让你的应用更快,异 ...

  10. PHP生成制作验证码

    看完就会,不会你打我,话不多说.开搞(人狠话不多) 1.0 首先先看代码 <?php header("Content-Type:text/html;Charset=UTF-8" ...