critnos
2020-06-27 12:34:38
打表,是一个信息学专用术语,意指对一些题目,通过打表技巧获得一个有序表或常量表,来执行程序某一部分,优化时间复杂度。这种算法也可用于在对某种题目没有最优解法时,用来得到分数的一种策略。——百度百科
因为交上去的程序运行时间有严格限制,但是本机运行则时间可以很长。所以提前用本机算出所有可能的数据的答案,拷贝到代码里。交上去的程序只用查表格就能得到答案。
这种方法适用于数据范围较小的情况。当然,本机的运行时间也要可以接受。
打表的代码,一般来说是时间可以接受,但又过不了的题目。比如跑
如果正解相当复杂,而打表又可以获得较高的分数甚至满分,在时间紧迫的赛场上是非常有用的。
一般来说打表分为两部分:打表程序和提交程序。
例子:P1035 级数求和
数据范围只有
#include<bits/stdc++.h>
using namespace std;
int f(int k)
{
double i=1,s=0;
while(s<=k)
{
s=s+1/i;
i++;
}
return i-1;
}
int main()
{
for(int i=1;i<=15;i++)
cout<<f(i)<<',';
}
运行后得到这样的结果:
2,4,11,31,83,227,616,1674,4550,12367,33617,91380,248397,675214,1835421,
怎么操作这个东西呢?拷贝到代码里。
#include<bits/stdc++.h>
using namespace std;
int a[]={2,4,11,31,83,227,616,1674,4550,12367,33617,91380,248397,675214,1835421},k;
int main()
{
cin>>k;
cout<<a[k-1];
}
如:
这种式子。
一般来说,洛谷上的代码长度提交限制是 50k,CCF 是 100k。如果上面的
(根据我自己的经验打表一般就存不多于
然而,仔细想想……
真的有必要吗?
比如
分块打表就是这种思想。预处理出一部分的数值。以上面的式子为例,
其中
事实上,分块打表顾名思义,就是把数据范围分成多个块,预处理每一块的信息,对不满一块的直接暴力,和分块数据结构异曲同工。
假定块长为
复杂度为暴力计算的复杂度。
所以,分块打表是一种平衡了代码长度和时间复杂度的算法(技巧?)。
图略丑
例子:P4213 【模板】杜教筛(Sum)
假定您们随便爆切区间筛素数等等 orz
两问。莫比乌斯函数直接裸的区间筛,但欧拉函数的前缀和太大了。应该如何优化表的大小呢?
首先,为什么要优化表的大小呢?
一般来说,块长越小,表越大,程序运行速度越快。通过优化手段减小表的大小,可以通过减小块长达到优化程序效率的作用。
以欧拉函数为例:
你会发现欧拉函数一般是
然后大眼观察法发现,经过第一次优化,表的每一项十分接近。对于这种情况,可以用下面两种方法:
每个数都减去表的最小值。
相邻两项继续做差。
似乎差不多?不过第二项更普适些(如果表中有一两个极小值优化
还有如转换进制之类的,当然还是要按照数据的特性优化。
(插一个区间筛的小技巧)
对于上面的杜教筛板子,你会发现这东西数据范围达到了
这种东西为啥有练习题
P2567 [SCOI2010]幸运数字(简单)
P4213 【模板】杜教筛(Sum)
P2657 [SCOI2009] windy 数
P4318 完全平方数
P5325 【模板】Min_25筛(较难 49.37KB 的代码你没有看错)
这里给出 P4318 的代码实现