P6973 [NEERC2016]List of Primes
题意简述:将质数集合的所有子集按照子集和为第一关键字,字典序为第二关键字从小到大排序,求最终形成的字符串的第 \(l\sim r\) 个字符。
又是一道妙妙题。
首先考虑当 \(r\leq 10^5\) 时直接搜索,首先枚举子集和 \(i\),状态是 \(sum,len,lim\) 表示剩余子集和为 \(sum\),大小为 \(len\),接下来只能使用第 \(lim\) 个及以后的质数。边界是 \(sum=0\),表示找到一个符合题意的子集。
直接暴力跑大概可以过 \(r\leq 10^5\),不过有一个显然的剪枝:预处理 \(f_{i,j}\) 表示只用第 \(j\) 个及以后的质数能否拼出 \(sum=i\),爆搜下一个分支时可以快速判断是否合法,从而避免进入不合法的分支。这样一来复杂度就变成了线性 \(\mathcal{O}(r)\)。
\(r\leq 10^{18}\) 时,我们继续选择搜索。观察到 \(r-l\leq 10^5\),所以如果已经遍历过的子集长度总和 与 接下来的分支所形成的的子集长度总和 之和仍小于 \(l\),那么根本没有必要遍历该分支。对此,我们预处理 \(g_{i,j}\) 表示只用第 \(j\) 个及以后的质数拼出的所有子集的长度之和(不考虑两边的中括号和逗号 \(\texttt{[],}\)),同时更改 \(f_{i,j}\) 的定义:只用第 \(j\) 个及以后的质数拼出来的子集个数。这是一个显然的 DP:从大到小枚举 \(j\),那么有 \(f_{i,j}\gets f_{i,j+1}+f_{i+pr_j,j+1}\),\(g_{i,j}\gets g_{i,j+1}+(g_{i+pr_j,j+1}+(bit(pr_j)+2)\times f_{i+pr_j,j+1})\),其中 \(pr_j\) 表示第 \(j\) 个质数,\(bit(x)\) 表示 \(x\) 在十进制下的位数,有 \(+2\) 是因为两个数之间有长度为 \(2\) 的逗号 + 空格。同时你可以根据 DP 很容易地求出来 \(i\) 的上界大约在 \(2.1\times 10^3\) 左右,那么 \(j\) 只需要开到 \(350\) 即可。
爆搜时,我们记录一个全局变量 \(acc(umulation)\) 表示已经遍历过的子集长度,此外还要在状态中加入 \(slen\) 表示当前分支已经选择的质数的长度之和,即 \(\sum_{p\in \mathrm{chosen}}bit(p)\)(原因接下来会讲)。
首先判断搜索边界:\(sum\) 是否 \(=0\)。是 \(0\) 就表示遍历到了一个子集,将该子集计入答案并返回,否则根据已有的信息算出该分支接下来所能产生的所有子集的长度之和,与 \(acc\) 求和后看是否小于 \(l\),若小于,则直接更新 \(acc\) 并返回即可。否则继续搜索即可。
接着考虑到了搜索边界该干什么:如果遍历到了一个子集,那么一个字符一个字符地考虑:插入一个字符时,首先将 \(acc\) 自增 \(1\),如果 \(acc>r\) 那么退出程序;否则,如果 \(acc\geq l\) 那么输出该字符;否则啥也不干。
怎么根据已有的信息算出该分支的子集长度总和呢?因为能产生 \(f_{sum,lim}\) 个子集,因此 \(acc\) 长度加上:
- \(4f_{sum,lim}\) 表示边界的四个字符:\(\texttt{'[', ']', ',', ' '}\)。
- \((len-1)\times 2f_{sum,lim}\) 表示已经选择的 \(len\) 个数所产生的 \(len-1\) 个长度为 \(2\) 的间隔 \(\texttt{‘,’, ‘ ’}\)(代码中 \(len\) 的初始值为 \(1\),所以是 \(2len\))。
- \(g_{sum,lim}\)(这个就不用解释了吧 = . =)。
- \(slen\times f_{sum,lim}\) 表示已经选择的质数长度在所有 \(f_{sum,lim}\) 个子集中所贡献的长度之和,这也是我们要记录 \(slen\) 的原因。
时间复杂度 \(\mathcal{O}(r-l)\),也可以说是 \(\mathcal{O}(n\pi(n))\),其中 \(n\approx 2100\)。
此外,今天是 2021 年的七夕节(8.14),祝大家七夕快乐,早日脱单(大雾)!
const int N=2100+5;
const int P=350;
int cnt,vis[N],pr[N],bt[N];
ll f[N][P],g[N][P];
void init(){
for(int i=2;i<N;i++){
if(vis[i])continue;
pr[++cnt]=i,f[i][cnt]=1,g[i][cnt]=bt[i]=log10(i)+1;
for(int j=i+i;j<N;j+=i)vis[j]=1;
}
for(int i=cnt;i;i--){
int len=bt[pr[i]]+2;
for(int j=pr[i];j<N;j++)
f[j][i]+=f[j-pr[i]][i+1]+f[j][i+1],
g[j][i]+=g[j-pr[i]][i+1]+g[j][i+1]+f[j-pr[i]][i+1]*len;
}
}
ll l,r,acc,p[N];
string to_str(int x){
string s;
while(x)s+=x%10+'0',x/=10;
reverse(s.begin(),s.end());
return s;
}
void add(char s){
if(++acc>r)exit(0);
if(acc>=l)cout<<s;
}
void dfs(int sum,int len,int lim,ll slen){
if(!sum){
add('[');
for(int i=1;i<=len;i++){
string s=to_str(p[i]);
for(int j=0;j<s.size();j++)add(s[j]);
if(i<len)add(','),add(' ');
} add(']'),add(','),add(' ');
return;
}
ll nw=acc+(len*2+4+slen)*f[sum][lim]+g[sum][lim];
if(nw<l)return acc=nw,void();
len++;
for(int j=lim;j<=cnt;j++){
int res=sum-pr[j]; p[len]=pr[j];
if(res<0)break;
if(res==0||f[res][j])dfs(res,len,j+1,slen+bt[pr[j]]);
}
}
int main(){
init(),cin>>l>>r;
for(int i=2;i<N;i++)dfs(i,0,1,0);
return 0;
}
P6973 [NEERC2016]List of Primes的更多相关文章
- [LeetCode] Count Primes 质数的个数
Description: Count the number of prime numbers less than a non-negative number, n click to show more ...
- projecteuler Summation of primes
The sum of the primes below 10 is 2 + 3 + 5 + 7 = 17. Find the sum of all the primes below two milli ...
- leetcode-Count Primes 以及python的小特性
题目大家都非常熟悉,求小于n的所有素数的个数. 自己写的python 代码老是通不过时间门槛,无奈去看了看大家写的code.下面是我看到的投票最高的code: class Solution: # @p ...
- Count Primes - LeetCode
examination questions Description: Count the number of prime numbers less than a non-negative number ...
- [leetcode] Count Primes
Count Primes Description: Count the number of prime numbers less than a non-negative number, n click ...
- Count Primes
Count the number of prime numbers less than a non-negative number, n public int countPrimes(int n) { ...
- hduoj 4715 Difference Between Primes 2013 ACM/ICPC Asia Regional Online —— Warmup
http://acm.hdu.edu.cn/showproblem.php?pid=4715 Difference Between Primes Time Limit: 2000/1000 MS (J ...
- HDU 4715:Difference Between Primes
Difference Between Primes Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Jav ...
- hdu 4715 Difference Between Primes
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=4715 Difference Between Primes Description All you kn ...
随机推荐
- 【数据结构与算法Python版学习笔记】算法分析
什么是算法分析 算法是问题解决的通用的分步的指令的聚合 算法分析主要就是从计算资源的消耗的角度来评判和比较算法. 计算资源指标 存储空间或内存 执行时间 影响算法运行时间的其他因素 分为最好.最差和平 ...
- 关于takin-data,你想知道的都在这里(一)启动命令篇
通过docker部署体验takin的小伙伴都应该知道,在安装部署手册中有提到:在启动surge-deploy任务前,需要将启动命令中的ip参数替换为docker容器所在宿主机的ip,很多小伙伴都在这里 ...
- 【二食堂】Alpha- 发布声明
MarkTexting Alpha版本发布声明 今日我们的MarkTexting正式上线了! 功能介绍 我们实现了一个简单的文本标注生成知识图谱的网站,在Alpha阶段,我们实现的功能有: 登陆注册 ...
- BUAA2020软工作业(五)——软件案例分析
项目 内容 这个作业属于哪个课程 2020春季计算机学院软件工程(罗杰 任健) 这个作业的要求在哪里 软件案例分析作业 我在这个课程的目标是 进一步提高自己的编码能力,工程能力 这个作业在哪个具体方面 ...
- 2021.8.14考试总结[NOIP模拟39]
T1 打地鼠 全场就俩人没切,还有一个是忘关$freopen$了. $code:$ 1 #include<bits/stdc++.h> 2 #define rin register sig ...
- 攻防世界 杂项 11.simple_transfer
题目描述: 文件里有flag,找到它. 题目分析: 文件解压后是一个抓包流量分析文件,用 wireshark 打开后,用分组字节流搜索 flag,追踪 TCP 流,可以隐约看到有 file.pdf, ...
- Spring源码解读(二):Spring AOP
一.AOP介绍 面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP).OOP中模块化的关键单元是类,而在AOP中,模块化单元是方面.方面实现了诸如跨越多种类型和对象的事务 ...
- 你知道怎么从jar包里获取一个文件的内容吗
目录 背景 报错的代码 原先的写法 编写测试类 找原因 最终代码 背景 项目里需要获取一个excle文件,然后对其里的内容进行修改,这个文件在jar包里,怎么尝试都读取不成功,但是觉得肯定可以做到,因 ...
- cf 11A Increasing Sequence(水,)
题意: A sequence a0, a1, ..., at - 1 is called increasing if ai - 1 < ai for each i: 0 < i < ...
- JAVA笔记5__构造块、静态块/单例设计模式/继承/final关键字/super关键字
public class Main { { //构造块(在构造对象时调用,先于构造方法执行) System.out.println("我是构造块!"); } static{ //静 ...