POJ 3090 Visible Lattice Points

题意

给出了一个1001×1001的点阵,和查询q,你要求出从(0,0)点到(q,q)点有多少种不同的点到原点的斜率(也就是题目中所说的“可见”),原点到原点不算。

思路

很直接的就可以想到算出所有的点到原点的斜率,预处理出答案,我们的做法是:从原点开始,一圈一圈地向外扩张,对于每个点都计算一边斜率,然后确认该斜率是否出现过,没出现过的话,当前圈上的点到原点的不同的斜率个数+1,这样求出每一圈的数目,最后我们再求一个前缀和就好了。

时间很理想,是O(n^2*log(n))的,然而POJ上超时了。我拿到ideone上测速,是700ms,可以过的,可能是评测机不在状态吧ˊ_>ˋ。补救的方法就是打了个表,反正数据也很少。

然而这个题的精髓并不是这么水过去的,这个题其实是一个包装过了的法雷级数(Wikipedia::法里数列 可能需要科学上网),如果试着一圈圈地写一下斜率,就会发现每一圈正好满足分子和分母互质的特性,每一圈的个数其实是欧拉函数值,整个答案是欧拉函数前缀和。所以我们就有了O(n),的处理方法,详细的可以看这个题POJ 2478 Farey Sequence

明明是刚做过的题居然都没有反应过来看来我真的是太迟钝了呢。

代码(暴力解法)

 

POJ 2478 Farey Sequence

题意

介绍性的问题,法雷级数(Wikipedia::法里數列 可能需要科学上网)

简单暴力,给出一个n,求出对于1~n中每一个数,有多少个小于等于他的数与他互质,把这些数量加在一起是多少,其实就是求欧拉函数的前缀和。

思路

欧拉函数有两种得到的方法,一是使用分解因子的方法,得到单个数的欧拉函数值,时间复杂度O(sqrt(n) * log(n))。如下:

可以发现这样算的话如果只求一个数的欧拉函数还是不亏的,但是如果要求非常非常多的欧拉函数,时间就会爆炸,所以我们不可以在这个题用这种方法。

第二种方法是利用欧拉函数的若干性质: 欧拉函数线性筛法详解(Lytning),然后把素数的线性筛法和求欧拉函数结合到一起,如果要求非常多的欧拉函数,使用这种方法预处理是非常快的,而且还顺便求了素数,一石二鸟。

快速地得到欧拉函数值之后,前缀和什么的就是很简单的事情了。

代码

 

SPOJ SUBLEX Lexicographical Substring Search

题意

把一个字符串所有的不同的字串全拿出来,按字典序排序,第k小的字符串是什么样子的呢?

思路

字串问题当然是后缀自动机了。对于给出的字符串,建立一个后缀自动机,然后对后缀自动机对应的状态图进行一次拓扑排序,每个节点的深度就是节点上携带的mxn(当前状态对应的字串最长为多少位)。接下来我们从深度最大的节点开始往根部处理每一个节点,我们要求出的就是对于每个能走的边,所有接下来的以这条边对应的字母开头的字串数目,而节点上面存的是节点出边的边权之和。这样我们就得到了一个处理好的图,我们只要在这个图上面搜索,逼近k值就可以了。

因为SPOJ卡常数特别严重,所以拓扑排序要使用计数排序的方法,而且要开额外的数组纪录每一个节点所有的孩子来加快之后的搜索速度。

代码

 

POJ3352 Road Construction / POJ3177 Redundant Paths

题意

给你一个联通图,问你最少要添加多少条边才能把这个图变成一个双联通图。

思路

非常经典的边双联通分量缩点问题。找出图中所有的边双联通分量,把这些分量各自收缩为一个点,节点之间若有边,一定是割边,这样就得到了一棵由边联通分量缩成的点和割边组成的树,把一个树变成双联通图要添加的最少边数是(r+1)/2其中r是这棵树的节点数量,这是一个蛮重要的结论。

找边联通分量的方法有好多,可以找到所有割边之后,对于每个点启动一次dfs,搜索中屏蔽掉所有割边,这样就可以收割所有的边双联通分量,也可以对于每个点直接启动一次tarjan算法,在函数返回的过程中,收割掉所有low值一样的节点就可以得到边双联通分量。这份代码使用的是后一种方案。

代码

两个题一个数据中有重边,一个没有,这份代码考虑了这个问题,所以连边都能顺利过,双倍经验。

 

HDU4738 Caocao's Bridges

题意

给出一个图,和一堆边,不保证没有重边,每一个边上携带者一个权值,现在问你能不能删去一条边,使图不再联通。可以的话删去权值最小的那条边,并且输出这个权值。

注意(坑点)

因为题意的特殊性,如果图本来就不联通,输出0.如果可以满足条件但最小的权值恰好是0,要输出1。

思路

删一个边把图裂开这种要求很明显就是找图的割边了。寻找割边是很典型的问题,应该使用tarjan算法,但是一定要注意重边对于找割边的影响(因为割边删掉后,图应该会裂开,但是如果有重边,删掉一条,还有一条,图显然不会裂开),因此我们要对原来的方法稍作修改,首先是存图的方式,我们不能漏掉任意一条边,因此一个理想的方式就是链式前向星。链式前向星有一个很好用的特性,我们在创建无向边的时候实际上是创建了两条有向边,而这两条有向边在链式前向星的容器中的编号是相邻的,也就是说对于编号i的边i^1就是这条边对应的反向边,这样我们就可以非常方便地标记这条边是不是已经走过(vis_e[i] = vis_e[i^1] = true)。tarjan部分附带了注释。

代码

 

HDU5396 / 15多校9-1 Expression

题意

给出了一个算术表达式,表达式里面只会含有加减乘三种运算。我们每一次从中选取两个数字并执行这两个数字对应的运算,运算结果放回式子中继续进行运算,直到最后只剩下了一个数。让我们求的是每一种不同的选取方式所得到的结果的和。

注意

这个题没有做好的主要原因是没有领会到题目中关于不同的选取方式的定义,注意到题目中讲到只要有一轮选取的数字不一样,就视为不同。这句话的含义中有两个信息:一是允许重复,二是要分先后

思路

最开始确实是没什么思路的,后来看了看board发现并不是特别难的题。于是尝试拆解问题,我们可以从算数式的一小段入手,以sample的1 + 4 * 6 – 8 * 3为例。

只考虑1 + 4部分,只有一种配置方法,就是(1 + 4);
考虑1 + 4 * 6部分,我们发现这一部分可以看成两种情况:1 + (4 * 6)和(1 + 4) * 6,在这里面我们已经考虑了1 + 4区段,而4 * 6区段只有一种配置。这样1 + 4 * 6这个区段就有两种配置了。
接下来考虑1 + 4 * 6 – 8区段,要想得到这个区段的全部情况,我们应该考虑:
1 + (4 * 6 – 8),1 + 4 * (6 – 8),(1 + 4) * 6 – 8,(1 + 4 * 6) – 8这些情况,其中(4 * 6 – 8)又可以看成考虑(4 * 6) – 8,4 * (6 – 8)这两种情况,而(1 + 4 * 6)的对应情况我们先前已经算好了。
最后,考虑1 + 4 * 6 – 8 * 3,顺着上面的思路来,就可以拆分成:
1 + (4 * 6 – 8 * 3),1 + 4 * (6 – 8 * 3),(1 + 4) * 6 – (8 * 3),(1 + 4 * 6) – 8 * 3,(1 + 4 * 6 – 8) * 3,接下来再去拆分这五种情况中的子情况。。。

如果比较敏感的话应该已经发现这就是典型的区间dp了,在推方程的时候,我们是将所有子情况的答案累加,得到当前情况的答案,但这还不够,还要注意每种情况,因为选择顺序的关系,会有多重副本,而这个副本的数量其实蛮好求,就是阶乘,而还要注意,我们在讨论当前情况的时候,是将整个等待处理的算式区段分成了(左区段)算子(分界点)算子(右区段)这样看待的,那么先后的问题又来了,我们不光左右区段的答案自身要乘上阶乘,最后整个结果还要乘上一个组合数,因为左右区段的操作又是分先后的。

以下引自官方题解,注意第2段第2行的dp[l][r]实际上是dp[l][k]

 

QQ20150820-1@2x

代码

测试数据

 

SPOJ LCS Longest Common Substring

[等待填坑]

 

SPOJ LCS2 Longest Common Substring II

[详细题解等待填坑]

SPOJ真是业界奇葩

 

SPOJ SORTBIT Sorted bit squence

[等待填坑]

 

URAL 1057 Amount of Degrees

[ 等待填坑中]

 

Scroll to top