• 离散化,矩阵快速幂,广义矩阵乘法


    离散化

    对于一些数,我们只需要知道它们之间的大小关系,而不需要知道他们的值相差多少
    那么,对于一个数列1,50,666,8558555,9989898989而言
    其实我们可以把他们看作1,2,3,4,5
    他们的大小关系并没有发生改变
    那么这个1,2,3,4,5就是上述原数组离散化后的id
    离散化就是获取原数列的id,从而进行更方便的操作(比如建立线段树
    那么这个实现也并不困难
    对于一个用vector存储的数组
    我们只需要使用unique,lower_bound和erase就可以实现
    当然也可以使用set来代替unique和erase,只是会额外地占用更多的空间

    实现代码如下:

    1. int main(){
    2. vector<int> v = {1, 50, 666, 8598, 68414, 36, 57, 956,4,4,5,50,666};
    3. vector<int> v2 = v;
    4. sort(v.begin(), v.end());
    5. //unique会去除相邻的重复元素,将它们放到数组后方,并返回去除后的无重复数组的最后一个元素的下一个地址
    6. //erase会清除两个迭代器之间的元素
    7. //通过这一步,就相当于我们把原数组排序并去重了
    8. //这一步等价于把原数组里面的数一个一个地塞进set里
    9. v.erase(unique(v.begin(), v.end()),v.end());
    10. //去重以后,原本的数的离散化下标记就变成了他们的数组下标
    11. for (int i = 0; i < v2.size();i++){
    12. cout << lower_bound(v.begin(), v.end(), v2[i]) - v.begin()<
    13. }
    14. return 0;
    15. }

    矩阵快速幂:

    矩阵之间的乘法具有结合性,因此,我们可以采用普通快速幂的思想,对矩阵进行快速幂运算

    对矩阵进行快速幂运算的用途在哪里呢?

    对于一个线性的递推式,这里不妨设其为:

    a_n=Aa_{n-1}+Ba_{n-2}+Ca_{n-3}

    我们可以将其写成1行n列的矩阵形式,并找到一个矩阵A,使得\begin{bmatrix} a_n &a_{n-1} &a_{n-2} \end{bmatrix}= \begin{bmatrix} a_{n-1}&a_{n-2} & a_{n-3} \end{bmatrix}\ast A,从而将线性迭代的时间复杂度O(n)降低到O(logn)

    题目链接:P1939 矩阵加速(数列) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

    实现代码如下:

    1. #include
    2. #include
    3. #include
    4. #define ll long long
    5. using namespace std;
    6. ll mod = 1E9 + 7;
    7. //重载运算符版本
    8. typedef struct node{
    9. ll ma[4][4] = {0};
    10. //重载矩阵乘法
    11. struct node operator*(const struct node &b)const {
    12. struct node tmp;
    13. for (int k = 1; k <= 3;k++){
    14. for (int i = 1; i <= 3;i++){
    15. for (int j = 1; j <= 3;j++){
    16. tmp.ma[i][j] = (tmp.ma[i][j] + ma[i][k] * b.ma[k][j]) % mod;
    17. }
    18. }
    19. }
    20. return tmp;
    21. }
    22. } matrix;
    23. int main(){
    24. int T, n;
    25. cin >> T;
    26. while(T--){
    27. cin >> n;
    28. matrix ans, base;
    29. // ans初始化为单位矩阵
    30. // base初始化为A矩阵
    31. ans.ma[1][1] = ans.ma[2][2] = ans.ma[3][3] = 1;
    32. base.ma[1][1] = base.ma[1][2] = base.ma[2][3] = base.ma[3][1] = 1;
    33. //快速幂
    34. if (n <= 3){
    35. cout << 1 << endl;
    36. continue;
    37. }
    38. n -= 3;
    39. while (n > 0)
    40. {
    41. if (n & 1)
    42. {
    43. ans = base * ans;
    44. }
    45. base = base * base;
    46. n >>= 1;
    47. }
    48. ll tmp[4] = {0};
    49. ll ma[4]={0,1,1,1};
    50. // a3,a2,a1;
    51. for (int i = 1; i <= 3;i++){
    52. for (int j = 1; j <= 3;j++){
    53. tmp[i] = (tmp[i] + ma[j] * ans.ma[j][i]) % mod;
    54. }
    55. }
    56. cout<1]<
    57. }
    58. return 0;
    59. }

    广义矩阵乘法

    我们现在定义两种运算方法opt1和opt2

    现在先考察opt1为乘法,而opt2为加法的情况

    这个时候乘法对加法满足分配律

    因为a*(b+c)=a*b+a*c

    那么我们再不带证明地稍加推广

    即:

    若运算opt1对opt2满足分配律,那么其也满足矩阵的结合律

    我们现在考察opt1为加法,而opt2为max()函数

    那么a+max(b+c)=max(a+b,a+c)

    也就是说加法对max()运算满足分配律

    在这种情况下,我们将opt1和opt2相应地将矩阵乘法中的乘法和加法操作替换掉

    就得到了广义矩阵乘法,其满足矩阵的结合律,可以使用快速幂

    我们现在定义矩阵间&运算为广义矩阵乘法

    那么实现代码如下:

    1. #include
    2. #define ll long long
    3. using namespace std;
    4. const int N = 105;
    5. ll max(ll a, ll b){
    6. return a > b ? a : b;
    7. }
    8. int n, m;
    9. typedef struct node{
    10. ll ma[N][N] = {0};
    11. struct node operator&(const struct node &b)const{
    12. struct node tmp;
    13. for (int k = 1; k <= n;k++){
    14. for (int i = 1; i <= n;i++){
    15. for (int j = 1; j <= n;j++){
    16. tmp.ma[i][j] = max(tmp.ma[i][j], ma[i][k] + b.ma[k][j]);
    17. }
    18. }
    19. }
    20. return tmp;
    21. }
    22. } matrix;
    23. int main(){
    24. cin >> n >> m;
    25. matrix ans, base;
    26. for (int i = 1; i <= n;i++){
    27. //初始化单位矩阵
    28. ans.ma[i][n + 1 - i] = INT_MIN;
    29. //
    30. for (int j = 1; j <= n;j++){
    31. cin >> base.ma[i][j];
    32. }
    33. }
    34. while (m > 0)
    35. {
    36. if (m & 1)
    37. {
    38. ans = base & ans;
    39. }
    40. base = base & base;
    41. m >>= 1;
    42. }
    43. for (int i = 1; i <= n;i++){
    44. for (int j = 1; j <= n;j++){
    45. cout << ans.ma[i][j] << ' ';
    46. }
    47. cout << endl;
    48. }
    49. return 0;
    50. }

    那么广义矩阵乘法有什么用呢?

    我们可以将其用到带max/min运算的线性递推式中,并加速其递推过程

    如现在有两个递推式:

    f_{i,0}=g_{i,0}+max(f_{k,0},f_{k,1})=max(g_{i,0}+f_{k,0},g_{i,0}+f_{k,1})

    f_{i,1}=g_{i,1}+f_{k,0}=max(g_{i,1}+f_{k,0},-\infty )

    则我们可以构造矩阵:

    \begin{bmatrix} g_{i,0} & g_{i,0}\\ g_{i,1} &-\infty \end{bmatrix}\ast \begin{bmatrix} f_{k,0}\\ f_{k,1} \end{bmatrix}=\begin{bmatrix} f_{i,0}\\f_{i,1} \end{bmatrix}

    在以后动态DP中会用到这个式子

    这里不再赘述g与f的含义

  • 相关阅读:
    【框架源码篇 03】Spring源码手写篇-手写AOP
    PLsql 中好用的插件显示不出来
    1043 Is It a Binary Search Tree
    【机器学习】数据科学基础——机器学习基础实践(一)
    【无标题】
    Error: Cannot find module ‘node:util‘
    R语言生成字符串的所有成对组合:使用outer函数和paste函数生成所有字符串的成对组合、使用expand.grid函数获得所有组合对
    Java IO(一) File类
    【cocos creator 】生成六边形地图
    为虚拟网络提供敏捷负载均衡:Everoute LB 特性解读
  • 原文地址:https://blog.csdn.net/Se_ren_di_pity/article/details/140053903