这套题质量巨高,引出了很多很棒的思路~
T1.matrix
题目大意
给定一个$n$行$n$列的01矩阵$A$和一个$n$维的01向量$x$,有$m$个询问,每次给出一个正整数$k(k \leq 10^9)$,求$y = A^kx$。
$n \leq 1000$,$m \leq 100$
题解
正解是特征多项式转多项式快速幂,然而很难写......
那么就来记录一下一个可过的巧妙做法:
首先,考虑到是01矩阵,可以采用bitset,单次矩阵乘法复杂度降为$O(\frac{n^3}{\omega})$。
其次,考虑预处理$A$的$2$的阶乘次幂的所有矩阵。
由于行向量与矩阵相乘时单次矩阵乘法复杂度降为$O(n^2)$,因此可以将总体复杂度降至$O(\frac{n^2}{\omega})$。
此时,总复杂度为$O(\frac{n^3}{\omega}logk+\frac{n^2}{\omega}mlogk)$,实际用时$3$秒左右。
可以发现,瓶颈在于预处理。
考虑这样一个优化:
原本矩阵乘法的代码是这样的:
//lena和lenb为矩阵的长和宽
//a[]为原矩阵,b[]为原矩阵的转置矩阵,set(i,j,k)会将矩阵的i行j列置为k
inline matrix operator * (matrix o)const
{
matrix ret(lena,o.lenb);
for(int i=0;i<lena;i++)
for(int j=0;j<o.lenb;j++)
ret.set(i,j,((a[i]&o.b[j]).count()&1));
return ret;
}
注意那个$count()$函数,根据DEV-C++找到的源代码显示,它的实现是这样的:
size_t _M_do_count() const _GLIBCXX_NOEXCEPT
{
size_t __result = 0;
for (size_t __i = 0; __i < _Nw; __i++)
__result += __builtin_popcountl(_M_w[__i]);
return __result;
}
可以发现,它只是对封装好的数组内的每个元素进行了一次$__builtin_popcountl()$而已......
这玩意只能说是巨慢啊!
于是这么写:
inline matrix operator * (matrix o)const
{
matrix ret(lena,o.lenb);
for(int i=0;i<lena;i++)
for(int j=0;j<lenb;j++)
if(a[i][j])
ret.a[i]^=o.a[j];
return ret;
}
然后就A掉了......
适用于所有运算符是这个样子的01矩阵乘法~
T2.easy
题目大意
在二维平面上从$(0.0)$走到$(n, m)$,在$(x, y)$时,可以以$A_x$的代价
走到$(x, y+1)$或者以$B_y$的代价走到$(x+1, y)$,求最小代价。
$1 \leq n,m \leq 500000$
题解
神题。太神了。
首先,有个60分的部分分是$B_i=i$。
让咱们推一波式子:
Ax ----------
| |
| |
Ay ---------
c d
如上图,目标是从左上角走到右上角。
图上有两种方案:沿着$A_x$走到$d$,再向下,和在$c$处转向,然后沿着$A_y$走到$d$。
两种方案的差值如下:
$$ (d-c)A_x+(y-x)d - (y-x)c-(d-c)A_y $$
$$= (d-c)(A_x-A_y+y-x) $$
若前者更优,那么显然值为负且越小越好,否则值为正且越大越好。
此时,由于$A_x-A_y+y-x$为一定值,且只有这个值可以为负,那么$d-c$无论谁更优都一定是越大越好。
于是得出结论,这种情况下的路线满足所有的$y+1$都在同一行进行,枚举即可。
---------------------------------------以上为本蒟蒻和大佬的分割线-----------------------------
正解更加巧妙。
考虑最优解可能的分布情况,把所有可能的转向的位置处理出来。
考虑这样一种情况:
Ax ----------
| |
Ay ------
|
Az ---
Bc Bd
假设确定了$A_x$和$A_z$(可以认为是边界)可能出现在最优解上,那么若$A_y$想要加入可能的解集,那就有如上图的一条更优路径。
那么再次推式子:
$$(d-c)A_x+(y-x)B_d \geq (y-x)B_c+(d-c)A_y$$
$$(d-c)(A_x-A_y) \geq (y-x)(B_c-B_d)$$
$$\frac{A_y-A_x}{y-x} \leq \frac{B_d-B_c}{d-c}$$
令右半边为斜率,于是获得了一只野生的斜率式。
于是可以发现,可能的最优转向位置构成一个下凸壳。
那么首先对于两维都分别预处理出这个凸壳~
然而即使处理出这两个凸壳,最坏点数也还是$O(n^2)$的.....
不过考虑如下情况:
Ax ----------
| |
Ay ------
Bc Bd
可以证明,若当前位置为$(c,x)$,那么直接选择$(d,y)$为决策点,当$\frac{A_y-A_x}{y-x} \leq \frac{B_d-B_c}{d-c}$成立,选择向上更优,否则向右更优。
然后$O(n)$贪心即可~
T3.game
题目大意
两人在一个二维棋盘上轮流移动一个棋子,每次可将棋子从$(x,y)$移动到$(x-a,y)$或$(x,y-a)$,其中$a$为任意实数,但满足移动路径上不能碰到障碍,最后无法操作者输,多组询问求以某个坐标为起点的情况下,最优决策下哪一方必胜。
障碍数$0 \leq n \leq 10^5$,坐标范围$0 \leq x,y \leq 10^9$。
题解
用$SG$函数打张表可发现,一个点为先手必败,当且仅当它无法在一次操作以内到达另一个先手必败点。
那么可以发现这些先手必败点会被障碍分割成一个个小段。
于是一个扫描线+一个数据结构维护一下当前行每个位置下面是否能直接到达先手必败点即可。
按当前行的障碍将当前行的所有位置分开处理即可。