zoukankan      html  css  js  c++  java
  • ACM之路(18)—— 矩阵

      矩阵是干什么的呢?一句话来说就是,知道相邻两个函数的递推关系和第一个数,让你递推到第n个数。显然,如果n很大,那么一个一个递推过去是会超时的。所以矩阵就是用来解决这种快速递推的问题的。

      比方说斐波那契数列就是一个递推的典型。

      先丢题目链接:我是题目!

      那么问题的关键就变成了如何找递推关系的中介矩阵temp了。如果题目告诉了你递推关系,题目就变得很简单了。但是告诉你递推关系大致可分为两类,一类是加法的递推,像斐波那契数列,temp矩阵的每个元素都是常数。另外一种,就是有乘法的递推关系了,这类关系一般需要凑出来,有时候隐晦的话也不是那么容易的。比如说上面题目中的I题,就是需要拼凑的。

      总之先交代模板,先看H题。这题求矩阵sum(A)=A^1+A^2+A^3+...+A^n。方法的话是二分,左边这个式子如果n是偶数的话可以拆成(1+A^(n/2))*(A^1+A^2+...+A(n/2)),然后右边部分又可以递归,不断二分地递归即可。当然,如果n是奇数要再加上A^n;另外上面式子中的1在这里指的是同阶的单位矩阵。代码如下(也可以直接当做矩阵的模板了):

      1 #include <stdio.h>
      2 #include <algorithm>
      3 #include <math.h>
      4 #include <vector>
      5 #include <map>
      6 #include <set>
      7 #include <iostream>
      8 #include <string.h>
      9 #pragma comment(linker, "/STACK:1024000000,1024000000")
     10 using namespace std;
     11 typedef long long ll;
     12 typedef pair<int,int> pii;
     13 const int mod = 10;
     14 
     15 //int mod;
     16 
     17 void add(int &a,int b)
     18 {
     19     a += b;
     20     if(a < 0) a += mod;
     21     //if(a >= mod) a -= mod;
     22     a %= mod;
     23 }
     24 
     25 struct matrix
     26 {
     27     int e[50+5][50+5],n,m;
     28     matrix() {}
     29     matrix(int _n,int _m): n(_n),m(_m) {memset(e,0,sizeof(e));}
     30     matrix operator * (const matrix &temp)const
     31     {
     32         matrix ret = matrix(n,temp.m);
     33         for(int i=1;i<=ret.n;i++)
     34         {
     35             for(int j=1;j<=ret.m;j++)
     36             {
     37                 for(int k=1;k<=m;k++)
     38                 {
     39                     add(ret.e[i][j],1LL*e[i][k]*temp.e[k][j]%mod);
     40                 }
     41             }
     42         }
     43         return ret;
     44     }
     45     matrix operator + (const matrix &temp)const
     46     {
     47         matrix ret = matrix(n,m);
     48         for(int i=1;i<=n;i++)
     49         {
     50             for(int j=1;j<=m;j++)
     51             {
     52                 add(ret.e[i][j],(e[i][j]+temp.e[i][j])%mod);
     53             }
     54         }
     55         return ret;
     56     }
     57     void getE()
     58     {
     59         for(int i=1;i<=n;i++)
     60         {
     61             for(int j=1;j<=m;j++)
     62             {
     63                 e[i][j] = i==j?1:0;
     64             }
     65         }
     66     }
     67 };
     68 
     69 matrix qpow(matrix temp,int x)
     70 {
     71     int sz = temp.n;
     72     matrix base = matrix(sz,sz);
     73     base.getE();
     74     while(x)
     75     {
     76         if(x & 1) base = base * temp;
     77         x >>= 1;
     78         temp = temp * temp;
     79     }
     80     return base;
     81 }
     82 
     83 void print(matrix p)
     84 {
     85     int n = p.n;
     86     int m = p.m;
     87     for(int i=1;i<=n;i++)
     88     {
     89         for(int j=1;j<=m;j++)
     90         {
     91             printf("%d ",p.e[i][j]);
     92         }
     93         cout << endl;
     94     }
     95 }
     96 
     97 matrix solve(matrix a, int k)
     98 {
     99     if(k == 1) return a;
    100     int n = a.n;
    101     matrix temp = matrix(n,n);
    102     temp.getE();
    103     if(k & 1)
    104     {
    105         matrix ex = qpow(a,k);
    106         k--;
    107         temp = temp + qpow(a,k/2);
    108         return temp * solve(a,k/2) + ex;
    109     }
    110     else
    111     {
    112         temp = temp + qpow(a,k/2);
    113         return temp * solve(a,k/2);
    114     }
    115 }
    116 
    117 int main()
    118 {
    119     int n,k;
    120     while(scanf("%d%d",&n,&k)==2 && n)
    121     {
    122         matrix a = matrix(n,n);
    123         for(int i=1;i<=n;i++)
    124         {
    125             for(int j=1;j<=n;j++) {scanf("%d",&a.e[i][j]);a.e[i][j] %= mod;} 
    126             // 这里矩阵的每个元素在输入以后就要mod一下,不然可能会因为输入的元素太大导致在后面爆int(?)
    127         }
    128         matrix ans = solve(a,k);
    129         for(int i=1;i<=n;i++)
    130         {
    131             for(int j=1;j<=n;j++)
    132             {
    133                 printf("%d%c",ans.e[i][j],j==n?'
    ':' ');
    134             }
    135         }
    136         puts("");
    137     }
    138 }
    矩阵模板

      还要讲的几点是:1.E题中的类型,A是1000*6的矩阵,B是6*1000的矩阵,现在求(A*B)^N,显然1000*1000的矩阵是要超时的,那么不妨化成A*(B*A)^(n-1)*B,这样B*A是6*6的矩阵,就快多了;2.矩阵不要无脑开的很大,因为我发现有时候矩阵开大了时间会增加很多;3.关于循环矩阵的问题,比方说一个矩阵,它的每一行都是和第一行一样的,只是位置每行错开一个位置(当然列也是一样),这样的矩阵就叫做循环矩阵,在计算的过程中只要保存一行即可(即使是一行也可以知道整个矩阵的内容了),这样的题目出现在J题。这题的代码如下:

      1 #include <stdio.h>
      2 #include <algorithm>
      3 #include <math.h>
      4 #include <vector>
      5 #include <map>
      6 #include <set>
      7 #include <iostream>
      8 #include <string.h>
      9 #pragma comment(linker, "/STACK:1024000000,1024000000")
     10 using namespace std;
     11 typedef pair<int,int> pii;
     12 //const int mod = 1000;
     13 
     14 int n,mod,d,k;
     15 
     16 void add(int &a,int b)
     17 {
     18     a += b;
     19     if(a < 0) a += mod;
     20     //if(a >= mod) a -= mod;
     21     a %= mod;
     22 }
     23 
     24 struct matrix
     25 {
     26     int e[5][500+5],n,m;
     27     matrix() {}
     28     matrix(int _n,int _m): n(_n),m(_m) {memset(e,0,sizeof(e));}
     29     matrix operator * (const matrix &temp)const
     30     {
     31         matrix ret = matrix(1,m);
     32         for(int i=0;i<m;i++)
     33         {
     34             for(int j=0;j<m;j++)
     35             {
     36                 add(ret.e[0][i],1LL*e[0][j]*temp.e[0][((j-i)%m+m)%m]%mod);
     37             }
     38         }
     39         return ret;
     40     }
     41 
     42 };
     43 
     44 matrix qpow(matrix temp,int x)
     45 {
     46     int sz = temp.m;
     47     matrix base = matrix(1,sz);
     48     base.e[0][0] = 1;
     49     while(x)
     50     {
     51         if(x & 1) base = base * temp;
     52         x >>= 1;
     53         temp = temp * temp;
     54     }
     55     return base;
     56 }
     57 
     58 void print(matrix p)
     59 {
     60     int n = p.n;
     61     int m = p.m;
     62     for(int i=0;i<n;i++)
     63     {
     64         for(int j=0;j<m;j++)
     65         {
     66             printf("%d ",p.e[i][j]);
     67         }
     68         cout << endl;
     69     }
     70 }
     71 
     72 int main()
     73 {
     74     while(scanf("%d%d%d%d",&n,&mod,&d,&k)==4)
     75     {
     76         matrix ans = matrix(1,n);
     77         for(int i=0;i<n;i++) scanf("%d",&ans.e[0][i]);
     78         matrix temp = matrix(1,n);
     79         //for(int i=0;i<n;i++)
     80         {
     81             temp.e[0][0]=1;
     82             for(int j=1;j<=d;j++)
     83             {
     84                 int now = 0+j;
     85                 if(now >= n) now -= n;
     86                 temp.e[0][now] = 1;
     87             }
     88             for(int j=1;j<=d;j++)
     89             {
     90                 int now = 0-j;
     91                 if(now < 0) now += n;
     92                 temp.e[0][now] = 1;
     93             }
     94         }
     95         //print(temp);
     96         temp = qpow(temp,k);
     97         ans = ans * temp;
     98         for(int i=0;i<n;i++)
     99         {
    100             printf("%d%c",ans.e[0][i],i==n-1?'
    ':' ');
    101         }
    102     }
    103 }
    循环矩阵

    要注意之所以这份代码里面的qpow函数的单位矩阵可以直接相乘,是因为单位矩阵也是一个循环矩阵,且循环性质和题中所给的矩阵一样。

      另外,这份题目中其他题目的好题如下:B,C,M,N,P,R。

      以上几题想稍微补充一下的是,M题,ceil里面的数加上它的共轭函数刚好是一个整数,证明如下:展开以后,有根号b的部分,如果其次数是偶数,那么一定是整数,如果是奇数,那么共轭的两个这个部分刚好一正一负,相抵消。这点证明了以后,又因为(a-1) 2< b < a2,因此(a-根号b)在0到1之间,所以共轭的两部分相加刚好是前面部分取ceil的值,即所求的没有%m的部分。对R题,虽然题目不算难,但是要注意的是,A^B%C,不能直接取余,要结合数论中的知识来,如下:

    1 /*
    2     A^B %C   这题的C是质数,而且A,C是互质的。
    3     所以直接A^(B%(C-1)) %C
    4 
    5     比较一般的结论是 A^B %C=A^( B%phi(C)+phi(C) ) %C , B>=phi(C)
    6 */

      最后想说的是,D题和L题并没有做,而且P题最后部分也没法很好的证明,P题还是留个代码吧:

      1 #include <stdio.h>
      2 #include <algorithm>
      3 #include <math.h>
      4 #include <vector>
      5 #include <map>
      6 #include <set>
      7 #include <iostream>
      8 #include <string.h>
      9 using namespace std;
     10 typedef long long ll;
     11 typedef pair<int,int> pii;
     12 //const int mod = 1e9 + 7;
     13 
     14 int mod;
     15 
     16 void add(int &a,int b)
     17 {
     18     a += b;
     19     if(a < 0) a += mod;
     20     //if(a >= mod) a -= mod;
     21     a %= mod;
     22 }
     23 
     24 struct matrix
     25 {
     26     int e[10][10];
     27     int n,m;
     28     matrix() {}
     29     matrix(int _n,int _m): n(_n),m(_m) {memset(e,0,sizeof(e));}
     30     matrix operator * (const matrix &temp)const
     31     {
     32         matrix ret = matrix(n,temp.m);
     33         for(int i=1;i<=ret.n;i++)
     34         {
     35             for(int j=1;j<=ret.m;j++)
     36             {
     37                 for(int k=1;k<=m;k++)
     38                 {
     39                     add(ret.e[i][j],1LL*e[i][k]*temp.e[k][j]%mod);
     40                 }
     41             }
     42         }
     43         return ret;
     44     }
     45     matrix operator + (const matrix &temp)const
     46     {
     47         matrix ret = matrix(n,m);
     48         for(int i=1;i<=n;i++)
     49         {
     50             for(int j=1;j<=m;j++)
     51             {
     52                 add(ret.e[i][j],(e[i][j]+temp.e[i][j])%mod);
     53             }
     54         }
     55         return ret;
     56     }
     57     void getE()
     58     {
     59         for(int i=1;i<=n;i++)
     60         {
     61             for(int j=1;j<=m;j++)
     62             {
     63                 e[i][j] = i==j?1:0;
     64             }
     65         }
     66     }
     67 };
     68 
     69 matrix qpow(matrix temp,int x)
     70 {
     71     int sz = temp.n;
     72     matrix base = matrix(sz,sz);
     73     base.getE();
     74     while(x)
     75     {
     76         if(x & 1) base = base * temp;
     77         x >>= 1;
     78         temp = temp * temp;
     79     }
     80     return base;
     81 }
     82 
     83 void print(matrix p)
     84 {
     85     int n = p.n;
     86     int m = p.m;
     87     for(int i=1;i<=n;i++)
     88     {
     89         for(int j=1;j<=m;j++)
     90         {
     91             printf("%d ",p.e[i][j]);
     92         }
     93         cout << endl;
     94     }
     95 }
     96 
     97 void Set(matrix &temp,int x,int y,int op)
     98 {
     99     temp.e[x][y] = op;
    100 }
    101 
    102 matrix solve(matrix temp,int x)
    103 {
    104     if(x==1) return temp;
    105     matrix ret = matrix(2,2);
    106     ret.getE();
    107     if(x & 1)
    108     {
    109         return (ret+qpow(temp,x/2)) * solve(temp,x/2) + qpow(temp,x);
    110     }
    111     else
    112     {
    113         return (ret+qpow(temp,x/2)) * solve(temp,x/2);
    114     }
    115 }
    116 
    117 void getAns(int r) {
    118     //printf("Yes
    ");
    119     int ans[205][205];
    120     memset(ans, -1, sizeof(ans));
    121     for (int i = r / 2 + 1; i <= r; i++)
    122         ans[i][i - (r / 2)] = 0;
    123     for (int i = r / 2 + 2; i <= r; i++)
    124         for (int j = 1; j <= (i - r / 2 - 1); j++)
    125             ans[i][j] = 1;
    126     for (int i = r / 2 + 1; i <= r; i++)
    127         for (int j = r - i + 1; j <= r; j++)
    128             ans[j][i] = 1;
    129     for (int i = 1; i <= r; i++) {
    130         for (int j = 1; j <= r; j++) {
    131             printf("%d", ans[i][j]);
    132             printf(j == r ? "
    " : " ");
    133         }
    134     }
    135 }
    136 
    137 int main()
    138 {
    139     /*
    140         http://www.cnblogs.com/Torrance/p/5412802.html
    141     */
    142     
    143     int n;
    144     int T;cin >>T;
    145     for(int kase=1;kase<=T;kase++)
    146     {
    147         scanf("%d%d",&n,&mod);
    148         printf("Case %d: ",kase);
    149         /*matrix ans = matrix(1,2);
    150         ans.e[1][1] = ans.e[1][2] = 1;
    151 
    152         matrix temp = matrix(2,2);
    153         temp.e[1][2] = temp.e[2][1] = temp.e[2][2] = 1;
    154         temp = solve(temp,n-1);
    155 
    156         matrix t = matrix(2,2);t.getE();
    157         temp = temp + t;
    158 
    159         ans = ans * temp;
    160         int sum = ans.e[1][1];*/
    161 
    162         // 上面的方法是这样的:
    163         /*
    164             (F1,F2)* temp(构造矩阵) = (F2,F3)
    165             同理的:(F1,F2)* temp^2 = (F3,F4)
    166             ...
    167             那么只要(F1,F2)*(temp^0+temp^1+...+temp^(n-1)),这个矩阵的第一个元素即是sum
    168             这是矩阵的分配律。而后面部分只要二分即可求得
    169         */
    170 
    171         // 下面是常规的方法(?)
    172         /*
    173             (F1,F2,sum(初始化为F1))*temp^(n-1)
    174             sum可以理解为加到F1(第一个元素)为止的元素的和,第二个元素可以理解为当前要加的元素
    175             temp为:
    176             0 1 0
    177             1 1 1
    178             0 0 1
    179         */
    180 
    181         matrix ans = matrix(1,3);
    182         for(int i=1;i<=3;i++) Set(ans,1,i,1);
    183         matrix temp = matrix(3,3);
    184         Set(temp,1,2,1);Set(temp,2,1,1);Set(temp,2,2,1);Set(temp,2,3,1);Set(temp,3,3,1);
    185 
    186         temp = qpow(temp,n-1);
    187         ans = ans * temp;
    188         int sum = ans.e[1][3];
    189 
    190         //cout << sum <<endl;
    191 
    192         cout << (sum%2 || sum ==0 ? "No":"Yes") <<endl;
    193         if(sum % 2 == 0 && sum != 0) getAns(sum);
    194     }
    195 }
    View Code

    D题有个很好的博客在这儿:D题。关于Q题我找出的递推关系如下:

    g(x)=g(x-1)+g(x-2)+1,(f(x)要调用一次,所以要加1)。

    似乎还有一种更好的方法但是我不是很理解。。

      那么,矩阵就暂时告一段落了~

    ————————————————隔日的分界线——————————————————————

      这里写于第二天,因为发现昨天总结的过于匆忙,有个东西忘记讲了。关于E题,这种既需要1000*6的矩阵又需要6*1000的矩阵的,如果是结构体,那么就需要开1000*1000从而照顾他们都能实现,但是1000*1000的话结构体是开不下的(程序会爆栈而直接崩溃)。所以这里需要一种新的写法,vector的写法,这样的话,想开多少开多少即可。但是我不是很喜欢这种写法,所以这里只是顺带提供一下这样写法的模板,以备不时之需。E题代码如下:

     1 #include <stdio.h>
     2 #include <algorithm>
     3 #include <math.h>
     4 #include <vector>
     5 #include <map>
     6 #include <set>
     7 #include <iostream>
     8 #include <string.h>
     9 using namespace std;
    10 typedef pair<int,int> pii;
    11 const int mod = 6;
    12 
    13 int n,k;
    14 
    15 typedef vector<int> vec ;
    16 typedef vector<vec> mat ;
    17 
    18 inline void add (int &a ,int b) {
    19     a += b ; if(a<0) a+=mod; a%=mod ;
    20 }
    21 
    22 mat mul (const mat &a , const mat &b) {
    23     mat ret(a.size(),vec(b[0].size())) ;
    24     for (int i=0 ; i<a.size() ; i++)
    25         for (int j=0 ; j<b[0].size() ; j++)
    26             for (int z=0 ; z<b.size() ; z++)
    27                 add(ret[i][j],a[i][z]*b[z][j]%mod) ;
    28     return ret ;
    29 }
    30 
    31 int main () {
    32     while(scanf("%d%d",&n,&k)==2)
    33     {
    34         if(n==0 && k==0) break;
    35         mat a(n,vec(k));
    36         mat b(k,vec(n));
    37         for(int i=0;i<n;i++)
    38         {
    39             for(int j=0;j<k;j++) scanf("%d",&a[i][j]);
    40         }
    41         for(int i=0;i<k;i++)
    42         {
    43             for(int j=0;j<n;j++) scanf("%d",&b[i][j]);
    44         }
    45         mat c =mul(b,a);
    46         mat base(c.size(),vec(c.size()));
    47         for(int i=0;i<c.size();i++) base[i][i] = 1;
    48         /*
    49             如果直接算A*B的话,得到的是1000*1000的矩阵,所以会一直超时。
    50             因为C^(n*n)=(A*B)^(n*n)=A*(B*A)^(n*n-1)*B,由于B*A是6*6的矩阵,再用矩阵快速幂来求就行了。
    51         */
    52         int temp = n*n-1;
    53         while(temp)
    54         {
    55             if(temp & 1) base = mul(base,c);
    56             temp >>= 1;
    57             c = mul(c,c);
    58         }
    59         mat ans = mul(mul(a,base),b);
    60         int sum = 0;
    61         for(int i=0;i<n;i++)
    62         {
    63             for(int j=0;j<n;j++) sum += ans[i][j];
    64         }
    65         cout << sum << endl;
    66     }
    67     return 0 ;
    68 }
    vector的矩阵写法

    ————————————————2017/9/4的分界线————————————————————

      考虑到矩阵稀疏的情况,有些题目(如poj3735)需要进行稀疏矩阵的乘法优化:若矩阵A*B,枚举A的每个位置A.e[i][j],若其不为0,则能做出贡献,在乘法A.e[i][j]*B.e[j][k]做出贡献,那么接着枚举k即可。

      另外上面的模板需要注意的一个地方是,由于每次乘法都会创造一个ret矩阵且伴随着一次memset,因此矩阵大小建议按照实际的大小设置(由于1base所以大小=size+1)否则可能出现TLE的情况。

  • 相关阅读:
    sequelize 批量添加和更新数据 bulkCreate
    js 线程和进程的关系
    mysql 索引 笔记1
    mysql 主键
    loj2292 「THUSC 2016」成绩单
    loj2291 「THUSC 2016」补退选
    cf984c Finite or not?
    cf984e Elevator
    loj2540 「PKUWC 2018」随机算法
    luoguT30204 偷上网
  • 原文地址:https://www.cnblogs.com/zzyDS/p/5705288.html
Copyright © 2011-2022 走看看