题面

SDOI2013 淘金

有一个 \(X\)、\(Y\) 轴坐标范围为 \(1\sim n\) 的范围的方阵,每个点上有块黄金。一阵风来 \((x,y)\) 上的黄金到了 \((f(x),f(y))\),\(f(x)\) 为 \(x\) 各位上数字的乘积,如果黄金飘出方阵就没了。求在 \(k\) 个格子上采集黄金最多可以采集的黄金数。

数据范围:\(1\le n\le 10^{12}\),\(k\le \min(n^2,10^5)\)。


蒟蒻语

蒟蒻跟着 \(\it srf\) 大师的日报来做这题,然后发现自己的裸代码跑得比题解都快,方法也比较神奇,于是来跟巨佬们讲讲。


蒟蒻解

首先众所周知,对于 \(1\le i\le 10^{12}\),\(f(i)\) 只有 \(8282\) 种,所以可以先找出这 \(8282\) 种 \(f(i)\),蒟蒻有三种方法:set、枚举质因数和数位 dp。

为了优化可以用数位 dp(这是第一次数位 dp),正好求出 \(1\sim n\) 的所有 \(f(i)\not=0\):

int dn,d[13],cnt=0;
ll t[N]; unordered_map<ll,int> nt;
bool vis[13][N];
void init(int w,ll now,bool ava){ //w:位 now:乘积 ava=true:可以自由选择数字
if(!~w){if(!nt.count(now)) nt[t[cnt]=now]=cnt,cnt++;return;}
if(vis[w][nt[now]]) return;
vis[w][nt[now]]=true;
int up=ava?9:d[w];
for(int i=1;i<=up;i++) init(w-1,now*i,true||i<up);
}
void INIT(){
while(n) d[dn++]=n%10,n/=10;
for(int i=0;i<=dn;i++) init(i-1,1,i<dn);
}

然后是最重要的部分:求每种 \(f(i)\) 有多少个 \(i\)。

蒟蒻原来的做法是对每种 \(f(i)\) 来一次数位 dp,瞬间被 TLE 打脸,于是蒟蒻想出了一个不同于别的巨佬的做法:

记 \(f_{w,now}\) 表示到第 \(w\) 位,剩下 \(w\) 位乘积为 \(now\) 的方案数。

用记忆化搜索转移,dp 中会用到除法。

ll f[13][N];
ll dp(int w,int now,bool ava){ //w:位 now:剩下期望乘积 ava=true:可以自由选择数字
if(!~w) return t[now]==1;
if(ava&&~f[w][now]) return f[w][now];
int up=ava?9:d[w]; ll res=0;
for(int i=1;i<=up;i++)if(t[now]%i==0)
res+=dp(w-1,nt[t[now]/i],ava||i<up);
if(ava) f[w][now]=res;
return res;
}
void DP(){
memset(f,-1,sizeof f);
for(int i=0;i<cnt;i++)
for(int j=1;j<=dn;j++) a[i]+=dp(j-1,i,j<dn); // ai 是每种 fi 出现次数
}

然后给 \(a_i\) 排个序,用堆维护找最大 \(k\) 乘积即可,考虑到相乘可能会爆 long long,蒟蒻用了除法。

时间复杂度 \(\Theta(8282*12*10)\)。


代码

很明显蒟蒻的题解都是废话,只好放代码了。

#include <bits/stdc++.h>
using namespace std; //Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define be(a) a.begin()
#define en(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f; //Data
const int N=8282;
const int mod=1e9+7;
ll n,a[N];
int k,nex[N],ans;
struct cmp{
bool operator()(pair<int,int> p,pair<int,int> q){
return 1.*a[p.x]/a[q.y]<1.*a[q.x]/a[p.y];
}
};
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> q; //Digitdp
int dn,d[13],cnt=0;
ll t[N]; unordered_map<ll,int> nt;
bool vis[13][N];
void init(int w,ll now,bool ava){
if(!~w){if(!nt.count(now)) nt[t[cnt]=now]=cnt,cnt++;return;}
if(vis[w][nt[now]]) return;
vis[w][nt[now]]=true;
int up=ava?9:d[w];
for(int i=1;i<=up;i++) init(w-1,now*i,true||i<up);
}
void INIT(){
while(n) d[dn++]=n%10,n/=10;
for(int i=0;i<=dn;i++) init(i-1,1,i<dn);
}
ll f[13][N];
ll dp(int w,int now,bool ava){
if(!~w) return t[now]==1;
if(ava&&~f[w][now]) return f[w][now];
int up=ava?9:d[w]; ll res=0;
for(int i=1;i<=up;i++)if(t[now]%i==0)
res+=dp(w-1,nt[t[now]/i],ava||i<up);
if(ava) f[w][now]=res;
return res;
}
void DP(){
memset(f,-1,sizeof f);
for(int i=0;i<cnt;i++)
for(int j=1;j<=dn;j++) a[i]+=dp(j-1,i,j<dn);
} //Main
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>k,INIT(),DP();
sort(a+0,a+cnt,[&](ll p,ll q){return p>q;});
for(int i=0;i<cnt;i++) nex[i]=0,q.push(mp(i,nex[i]++));
while(k--&&sz(q)){
pair<int,int> u=q.top(); q.pop();
(ans+=(a[u.x]%mod)*(a[u.y]%mod)%mod)%=mod;
if(nex[u.x]<cnt) u.y=nex[u.x]++,q.push(u);
}
cout<<ans<<'\n';
return 0;
}

祝大家学习愉快!

题解-SDOI2013 淘金的更多相关文章

  1. Bzoj 3131 [Sdoi2013]淘金 题解

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

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

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

  3. [BZOJ3131] [Sdoi2013]淘金

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

  4. 【bzoj 3131】[Sdoi2013]淘金

    Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块.    一阵风吹 ...

  5. SDOI2013 淘金

    题目链接:戳我 昨天做的题了,今天补一发题解. 是一个比较奇怪的数位DP.详细的我还是写代码注释里好了,感觉直接说不好描述. 代码如下: #include<iostream> #inclu ...

  6. bzoj 3131 [Sdoi2013]淘金(数位dp)

    题目描述 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块. 一阵风吹过,金子的位置发生了 ...

  7. P3303 [SDOI2013]淘金

    题目描述 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块. 一阵风吹过,金子的位置发生了 ...

  8. bzoj 3131: [Sdoi2013]淘金

    #include<cstdio> #include<iostream> #include<queue> #include<algorithm> #def ...

  9. bzoj 3131 [Sdoi2013]淘金(数位DP+优先队列)

    Description 小Z在玩一个叫做<淘金者>的游戏.游戏的世界是一个二维坐标.X轴.Y轴坐标范围均为1..N.初始的时候,所有的整数坐标点上均有一块金子,共N*N块.    一阵风吹 ...

随机推荐

  1. 蒲公英 · JELLY技术周刊 Vol.30: 此路不通?Vue 3 新提案 Ref-sugar

    蒲公英 · JELLY技术周刊 Vol.30 随着 Vue 3 发布,相关的新闻也逐渐火热起来,而近期 RFC 中两个新的提案也因为某乎上的一些事情变得广为人关注.Ref sugar和script s ...

  2. Java的强引用、软引用、弱引用、虚引用

    背景 工程中用到guava的本地缓存.它底层实现和API接口上使用了强引用.软引用.弱引用.所以温故知新下,也夯实下基础. 预备知识 先来看下GC日志每个字段的含义 Young GC示例解释 [GC ...

  3. BeatifulSoup在测试工作中的应用

    近期要做一个项目,重复性劳动比较多,小伙伴建议我用Jsoup,但是由于项目紧急,我直接选择了BeautifulSoup,关键原因是我Java语言不如Python掌握的熟练啊!所以,查了一圈它的中文文档 ...

  4. HTTP介绍(一)

    超文本传输协议(HTTP)是一种用于分布式,协作式超媒体信息系统的应用程序层协议.HTTP是万维网(World Wide Web)数据通信的基础,超文本文档包括指向用户可以轻松访问的其他资源的超链接, ...

  5. 关于mybatisPlus一些坑,当条件为null时

    1.TStaffDepart 属性有值是才匹配条件,会报错,相当于mybatis if 判断 eg:TStaffDepart staffDepart = new TStaffDepart();staf ...

  6. Hive 报错Class path contains multiple SLF4J bindings.

    进入hive报错信息如下 SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/ ...

  7. CSS z-index应用靠近用户的框

    1.z-index (1)z轴.数值越大,越靠近你 (2)只能作用于定位过的标签元素 应用示例 <!DOCTYPE html> <html lang="zh-CN" ...

  8. 协程实现爬虫的例子主要优势在于充分利用IO时间去请求其他的url

    # 分别使用urlopen和requests两个模块进行演示 # import requests # 需要安装的 # from urllib.request import urlopen # # ur ...

  9. 三步删除U深度,老毛桃,大白菜捆绑软件!!

    ①需要下载一个软件Fbinst Tool(万能U盘制作工具).链接:http://pan.baidu.com/s/1mim9Zxm 密码:7poo②插入U盘,直接打开Fbinst Tool.首先是U深 ...

  10. JUC并发工具包之CountDownLatch

    1.介绍 本文将介绍CountDownLatch并给出实践中的几个例子,通过使用CountDownLatch我们可以让一个线程阻塞直到其他一个或多个线程执行完成. A synchronization ...