T1:这题有一个十分巧妙的方法。
我们把每个f(n,x)称为x对答案的贡献,那么我们可以根据每个x对答案的贡献吧所有的x分成多段,每段里的f(n,x)相同,那么我们只需知道每段的两端的位置,就可以求出这一段的答案,把每一段的答案加起来就是最终答案。现在问题变成了如何求每一段的头、尾和贡献值。
设现在要求的一段的头位置为x1,尾位置为y1,贡献值为z1,前一段的这三个值为x0,y0,z0。
那么:
x1=y0+1
z1=n/x1-1
y1=n/(z1+1)
用循环不断求出想x,y,z,最后加上ans就行了。
T2:这题我的思路对了,但就是不会用栈来维护。
正着搜一遍,每次把当前高度入栈,判断是否有重复,没有的话就把ans加1(每次加入时要把比它高的栈去掉)。
T3:状压dp。
设f[i][k][j]表示到第i行,已经放了k个国王,第i行放置状态为j的方案数,则循环顺序如下:
1、枚举行
2、枚举i行状态
3、枚举i-1行转态
4、判断这两行状态是否合法
5、枚举当前放置国王数
6、判断这个国王数是否合法
7、状态转移
但这样会超时,所以我们要加一个优化。事先处理出合法状态,即本行的国王不可互相攻击的状态,之后直接枚举这些状态就行了。
T4:这题做起来十分麻烦。
1、我们先找一遍,如果某个格子的值在k和2k之间,那么直接输出这个格子,然后结束。
2、在经过上面的搜索之后,我们知道,当一个格子的值大于2k的话,那这个格子肯定是不可以选的,那么我们先把所有的值大于2k的格子标记为障碍。
3、把障碍标记完之后我们要找一个不包含障碍且和最大的子矩阵,如果这个子矩阵的和都小于2k的话,那就是无解,否则一定有解。关键是怎样照这个子矩阵。
如果要找这样一个 子矩阵,我们可以先枚举一个点,然后用这个点往外扩张。那么怎么进行扩张呢?我们要确定子矩阵的上下左右边界。
我们直接设左边界是这个点所在的列,那么右边界就是这个点右边的第一个障碍。为了快速求右边界,我们先预处理出一个数组,w[i][j]表示点(i,j)右边的第一个点的列的位置,求右边界时可以直接调用(求w数组时要把j倒着搜)。
现在我们要求上下边界,我们可以直接枚举,然后判断边界和当前行直接有没有障碍,注意,只有障碍的列在左边界和右边界直间才有影响。但这样会超时,这时我们可以用单调栈来维护。
我们每处理一列,就使用一次单调栈,每次都把w[i][j]如栈,把所有栈中列大于w[i][j]的都出栈,并且把w[i][j]所在的行作为这些点的下边界,在做完这项工作后,把栈中在w[i][j]的前一个位置的行作为w[i][j]的上边界,注意判断每一列处理完后栈中还有数的情况。
4、通过上面操作后,我们可以求出一个和最大的子矩阵。如果这个子矩阵的和小于2k,那么无解,否则一定有解,因为值在k和2k之间的格子在上面已经处理过了。现在的关键是如何求出最终矩阵的左上角和右下角。
我们还是按列处理。我们先算出每一列的和:
1、如果和在k和2k之间,那么我们直接输出。
2、如果和大于2k,那么我们在对列进行分割,也就是枚举这一列中的两个位置,算出这一列中这两个位置之间部分的和,在判断是否在k和2k之间。
3、如果小于k,则不管它。
如果子矩阵的和大于2k,那么在上面又找不出答案,那么答案肯定就是几行的和加起来。所以我们可以尝试把某几列连续的列的和求出来,判断是否在k和2k之间,这样一定能求出答案。