矩阵(kuangbin专题十九)

7 篇文章 0 订阅
6 篇文章 0 订阅

HDU-5015
题意:有一个矩阵横排第一行初始是0,233,2333,23333…,然后给出计算公式a[i][j] = a[i - 1][j] + a[i][j - 1],然后给出了n和m和第一列的各值要求计算a[n][m]的值。
在这里插入图片描述
可以得出
在这里插入图片描述

#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>

using namespace std;
typedef long long ll;
const int mod = 10000007;
int n, m;
struct Mat {
    int mat[12][12];
};

Mat operator*(Mat a, Mat b) {//重载*,矩阵乘模板
    Mat c;
    memset(c.mat, 0, sizeof c.mat);
    for (int i = 0; i <= n + 1; i++)
        for (int j = 0; j <= n + 1; j++)
            for (int k = 0; k <= n + 1; k++)
                c.mat[i][j] = (c.mat[i][j] + (ll)a.mat[i][k] * b.mat[k][j]) % mod;
    return c;
}

Mat qkpow(Mat a, int b) {
    Mat res;
    memset(res.mat, 0, sizeof res.mat);
    for (int i = 0; i <= n + 1; i++)res.mat[i][i] = 1;//初始化为单位矩阵
    while (b) {
        if (b & 1)res = res * a;
        a = a * a;
        b >>= 1;
    }
    return res;
}

int main() {
    while (~scanf("%d%d", &n, &m)) {
        Mat sum, tmp;
        memset(sum.mat, 0, sizeof sum.mat);
        memset(tmp.mat, 0, sizeof tmp.mat);
        sum.mat[0][0] = 23;
        sum.mat[n + 1][0] = 3;
        for (int i = 1; i <= n; i++)scanf("%d", &sum.mat[i][0]);
        if (m == 0) {
            if (n == 0)puts("0");
            else printf("%d\n", sum.mat[n][0]);
            continue;
        }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= i; j++)
                tmp.mat[i][j] = 1;
        for (int i = 0; i <= n; i++)tmp.mat[i][0] = 10;
        for (int i = 0; i <= n + 1; i++)tmp.mat[i][n + 1] = 1;
        sum = qkpow(tmp, m) * sum;
        printf("%d\n", sum.mat[n][0]);
    }
    return 0;
}

HDU-4990
构造方法一(直接构造):
s u m = sum= sum={ a [ 0 ] , 0 , 5 , 0 , 5 a[0],0,5,0,5 a[0],0,5,0,5}
t m p = tmp= tmp=
{{ 2 , 0 , 0 2,0,0 2,0,0},
{ 1 , 1 , 0 1,1,0 1,1,0}.
{ 1 , 0 , − 1 1,0,-1 1,0,1}
}

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
int n, mod;
struct Mat {
    ll mat[3][3];
} tmp;
Mat operator*(Mat a, Mat b) {
    Mat c;
    memset(c.mat, 0, sizeof c.mat);
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++)
            for (int k = 0; k < 3; k++)
                c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % mod;
    return c;
}
Mat qkpow(Mat a, int b) {
    Mat sum;
    memset(sum.mat, 0, sizeof sum.mat);
    for (int i = 0; i < 3; i++)sum.mat[i][i] = 1;
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}
int main() {
    while (~scanf("%d%d", &n, &mod)) {
        mod <<= 1;
        memset(tmp.mat, 0, sizeof tmp.mat);
        tmp.mat[0][0] = 2;
        tmp.mat[1][0] = tmp.mat[1][1] = tmp.mat[2][0] = 1;
        tmp.mat[2][2] = -1;
        tmp = qkpow(tmp, n);
        mod >>= 1;
        printf("%lld\n", ((tmp.mat[1][0] + tmp.mat[2][0] >> 1) % mod + mod) % mod);
    }
    return 0;
}

构造方法二:(先推公式再构造)
若n为偶数 a [ n ] = 2 ∗ a [ n − 1 ] = a [ n − 1 ] + 2 ∗ a [ n − 2 ] + 1 a[n]=2*a[n-1]=a[n-1]+2*a[n-2]+1 a[n]=2a[n1]=a[n1]+2a[n2]+1
若n为奇数 a [ n ] = 2 ∗ a [ n − 1 ] + 1 = a [ n − 1 ] + 2 ∗ a [ n − 2 ] + 1 a[n]=2*a[n-1]+1=a[n-1]+2*a[n-2]+1 a[n]=2a[n1]+1=a[n1]+2a[n2]+1
显然这两者相同
那么根据公式就好推了
s u m = sum= sum={ a [ 0 ] , a [ 1 ] , 1 a[0],a[1],1 a[0],a[1],1}
tmp=
{{0,2,0},
{1,1,0}.
{0,1,1}
}

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
typedef long long ll;
int n, mod;
struct Mat {
    ll mat[3][3];
}sum, tmp;

Mat operator*(Mat a, Mat b) {
    Mat c;
    memset(c.mat, 0, sizeof c.mat);
    for (int i = 0; i < 3; i++)
        for (int j = 0; j < 3; j++)
            for (int k = 0; k < 3; k++)
                c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % mod;
    return c;
}

Mat qkpow(Mat a, int b) {
    Mat sum;
    memset(sum.mat, 0, sizeof sum.mat);
    for (int i = 0; i < 3; i++)sum.mat[i][i] = 1;
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

int main() {
    while (~scanf("%d%d", &n, &mod)) {
        memset(tmp.mat, 0, sizeof tmp.mat);
        memset(sum.mat, 0, sizeof sum.mat);
        sum.mat[0][1]=sum.mat[0][2]=1;
        tmp.mat[0][1] = 2;
        tmp.mat[1][0] = tmp.mat[1][1] = tmp.mat[2][1] = tmp.mat[2][2] = 1;
        sum = sum * qkpow(tmp, n);
        printf("%d\n", sum.mat[0][0]);
    }
    return 0;
}

HDU - 4549
题意 F [ 0 ] = a , F [ 1 ] = b , F [ n ] = F [ n − 1 ] ∗ F [ n − 2 ] ( n > 1 ) F[0] = a,F[1] = b,F[n] = F[n-1] * F[n-2] ( n > 1 ) F[0]=aF[1]=bF[n]=F[n1]F[n2](n>1),求 F [ n ] F[n] F[n]
可以发现 F [ n ] = a f [ n − 1 ] ∗ b f [ n ] F[n]=a^{f[n-1]}*b^{f[n]} F[n]=af[n1]bf[n]
由于f[n]很大,根据欧拉公式
a x % p = a x % p h i [ p ] % p a^{x}\% p=a^{x\%phi[p]}\%p ax%p=ax%phi[p]%p
由于 p = 1 e 9 + 7 p=1e9+7 p=1e9+7,所以 p h i [ p ] = p − 1 phi[p]=p-1 phi[p]=p1

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;
typedef long long ll;
const int mod = 1000000007;
struct Mat {
    ll mat[2][2];
} sum, tmp;
ll a, b, n;

Mat operator*(Mat a, Mat b) {
    Mat c;
    memset(c.mat, 0, sizeof c.mat);
    for (int i = 0; i < 2; i++)
        for (int j = 0; j < 2; j++)
            for (int k = 0; k < 2; k++)
                c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % (mod - 1);
    return c;
}

Mat qkpow(Mat a, int b) {
    Mat sum;
    memset(sum.mat, 0, sizeof sum.mat);
    for (int i = 0; i < 2; i++)sum.mat[i][i] = 1;
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

ll qkpow(ll a, ll b) {
    ll sum = 1;
    while (b) {
        if (b & 1)sum = sum * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return sum;
}

int main() {
    while (~scanf("%lld%lld%lld", &a, &b, &n)) {
        memset(sum.mat, 0, sizeof sum.mat);
        memset(tmp.mat, 0, sizeof tmp.mat);
        sum.mat[0][1] = 1;
        tmp.mat[0][1] = tmp.mat[1][1] = tmp.mat[1][0] = 1;
        if (n == 0 || n == 1) {
            if (!n)printf("%lld\n", a);
            else printf("%lld\n", b);
            continue;
        }
        sum = sum * qkpow(tmp, n - 1);
        ll pa = sum.mat[0][0], pb = sum.mat[0][1];
        printf("%d\n", qkpow(a, pa) * qkpow(b, pb) % mod);
    }
    return 0;
}

HDU - 4965
若直接求 ( A ∗ B ) n ∗ n (A*B)^{n*n} (AB)nn内存会爆
( A ∗ B ) n ∗ n = ( A ∗ B ) ∗ ( A ∗ B ) … … ( A ∗ B ) = A ∗ ( B ∗ A ) ∗ ( B ∗ A ) … … ( B ∗ A ) ∗ B = A ∗ ( B ∗ A ) n ∗ n − 1 ∗ B (A*B)^{n*n}=(A*B)*(A*B)……(A*B)= A*(B*A)*(B*A)……(B*A)*B=A*(B*A)^{n*n-1}*B (AB)nn=(AB)(AB)(AB)=A(BA)(BA)BAB=A(BA)nn1B
B*A是一个最大6*6的矩阵,这样就不会爆内存了,且提升速度

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
typedef long long ll;
int n, m;

struct Mat {
    ll mat[6][6];

    void init(int x) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < 6; i++)mat[i][i] = x;
    }

    Mat operator*(Mat &a) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < m; i++)
            for (int j = 0; j < m; j++)
                for (int k = 0; k < m; k++)
                    c.mat[i][j] = (c.mat[i][j] + mat[i][k] * a.mat[k][j]) % 6;
        return c;
    }
} c;

int a[1010][6], b[6][1010], d[1010][6], res[1010][1010];

Mat qkpow(Mat a, int b) {
    Mat sum;
    sum.init(1);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

int main() {
    while (scanf("%d%d", &n, &m), n + m) {
        c.init(0);
        memset(d, 0, sizeof d);
        memset(res, 0, sizeof res);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                scanf("%d", &a[i][j]);
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                scanf("%d", &b[i][j]);
        for (int i = 0; i < m; i++)
            for (int j = 0; j < m; j++)
                for (int k = 0; k < n; k++)
                    c.mat[i][j] = (c.mat[i][j] + b[i][k] * a[k][j]) % 6;
        c = qkpow(c, n * n - 1);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                for (int k = 0; k < m; k++)
                    d[i][j] = (d[i][j] + a[i][k] * c.mat[k][j]) % 6;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                for (int k = 0; k < m; k++)
                    res[i][j] = (res[i][j] + d[i][k] * b[k][j]) % 6;
        int ans = 0;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                ans += res[i][j];
        cout << ans << endl;
    }
    return 0;
}

UVA - 10655
题意:给定 p , q p,q p,q分别表示 a + b , a ∗ b a+b,a*b a+b,ab的值,再给定n,求 a n + b n a^n+b^n an+bn
F [ n ] = a n + b n F[n]=a^n+b^n F[n]=an+bn
F [ n ] ∗ ( a + b ) = a n + 1 + b n + 1 + ( a ∗ b ) ( b n − 1 + a n − 1 ) = F [ n + 1 ] + q ∗ F [ n − 1 ] F[n]*(a+b)=a^{n+1}+b^{n+1}+(a*b)(b^{n-1}+a^{n-1}) =F[n+1]+q*F[n-1] F[n](a+b)=an+1+bn+1+(ab)(bn1+an1)=F[n+1]+qF[n1]
F [ n ] = F [ n − 1 ] ∗ p − q ∗ F [ n − 1 ] F[n]=F[n-1]*p-q*F[n-1] F[n]=F[n1]pqF[n1]
所以 s u m = F [ 0 ] , F [ 1 ] sum={F[0],F[1]} sum=F[0],F[1]
t m p = tmp= tmp=
{{ 0 , − q 0,-q 0,q},
{ 1 , p 1,p 1,p}
}

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
typedef long long ll;
ll p, q, n;

struct Mat {
    ll mat[2][2];

    void init(ll x) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < 2; i++)mat[i][i] = x;
    }

    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                for (int k = 0; k < 2; k++)
                    c.mat[i][j] += mat[i][k] * b.mat[k][j];
        return c;
    }
} sum, tmp;

Mat qkpow(Mat a, ll b) {
    Mat sum;
    sum.init(1);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

int main() {
    while (scanf("%lld%lld%lld", &p, &q, &n) == 3) {
        tmp.init(0);
        tmp.mat[0][1] = -q;
        tmp.mat[1][0] = 1, tmp.mat[1][1] = p;
        tmp = qkpow(tmp, n);
        printf("%lld\n", tmp.mat[0][0] * 2 + tmp.mat[1][0] * p);
    }
    return 0;
}

HDU-2256
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
const int mod = 1024;
typedef long long ll;
int t, n;

struct Mat {
    ll mat[2][2];

    void init(ll x) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < 2; i++)mat[i][i] = x;
    }

    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                for (int k = 0; k < 2; k++)
                    c.mat[i][j] = (c.mat[i][j] + mat[i][k] * b.mat[k][j]) % mod;
        return c;
    }
} tmp;

Mat qkpow(Mat a, ll b) {
    Mat sum;
    sum.init(1);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        memset(tmp.mat, 0, sizeof tmp.mat);
        tmp.mat[0][0] = 5;
        tmp.mat[0][1] = 2;
        tmp.mat[1][0] = 12;
        tmp.mat[1][1] = 5;
        tmp = qkpow(tmp, n - 1);
        printf("%d\n", (2 * (5 * tmp.mat[0][0] + 2 * tmp.mat[1][0]) - 1) % mod);
    }
    return 0;
}

HDU-4565
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
const int mod = 1024;
typedef long long ll;
ll a, b, m, n;

struct Mat {
    ll mat[2][2];

    void init(ll x) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < 2; i++)mat[i][i] = x;
    }

    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                for (int k = 0; k < 2; k++)
                    c.mat[i][j] = (c.mat[i][j] + mat[i][k] * b.mat[k][j]) % m;
        return c;
    }
} tmp;

Mat qkpow(Mat a, ll b) {
    Mat sum;
    sum.init(1);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

int main() {
    while (scanf("%d%d%d%d", &a, &b, &n, &m) == 4) {
        memset(tmp.mat, 0, sizeof tmp.mat);
        tmp.mat[0][0] = tmp.mat[1][1] = a;
        tmp.mat[0][1] = 1;
        tmp.mat[1][0] = b;
        tmp = qkpow(tmp, n - 1);
        printf("%d\n", 2 * (a * tmp.mat[0][0] + tmp.mat[1][0]) % m);
    }
    return 0;
}

UVA - 11149
题意,等比矩阵求和
思路:二分幂求和

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

using namespace std;
typedef long long ll;
int n, m;

struct Mat {
    ll mat[40][40];

    void init(ll x, int n) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < n; i++)mat[i][i] = x;
    }

    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                for (int k = 0; k < n; k++)
                    c.mat[i][j] = (c.mat[i][j] + mat[i][k] * b.mat[k][j]) % 10;
        return c;
    }

    Mat operator+(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                c.mat[i][j] = (mat[i][j] + b.mat[i][j]) % 10;
        return c;
    }
} a, q;

Mat qkpow(Mat a, ll b) {
    Mat sum;
    sum.init(1, n);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

void out(Mat a) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n - 1; j++)
            printf("%d ", a.mat[i][j]);
        printf("%d\n", a.mat[i][n - 1]);
    }
    puts("");
}

Mat sum(ll n) {
    if (n == 1)return a;
    Mat aa = qkpow(a, n + 1 >> 1);
    Mat s = (q + aa) * sum(n >> 1);
    if (n & 1)return s + aa;
    return s;
}

int main() {
    while (scanf("%d%d", &n, &m), n) {
        q.init(1, n);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                scanf("%lld", &a.mat[i][j]), a.mat[i][j] %= 10;
        out(sum(m));
    }
    return 0;
}

UVA - 11551
题意:给n个数和r。接在输入n行,表示每次将第i个数变成它后面几个位置的和。重复r次

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;
const int mod = 1000;
int t, n, m;

struct Mat {
    int mat[50][50];

    void init(int x, int n) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < n; i++)mat[i][i] = x;
    }

    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                for (int k = 0; k < n; k++)
                    c.mat[i][j] = (c.mat[i][j] + mat[i][k] * b.mat[k][j]) % mod;
        return c;
    }
} a, b;

Mat qkpow(Mat a, int b) {
    Mat sum;
    sum.init(1, n);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        b.init(0, n);
        a.init(0, n);
        for (int i = 0; i < n; i++)scanf("%d", &a.mat[0][i]);
        for (int i = 0, s, j; i < n; i++) {
            scanf("%d", &s);
            while (s--) {
                scanf("%d", &j);
                b.mat[j][i] = 1;
            }
        }
        if (!m) {
            for (int i = 0; i < n - 1; i++)printf("%d ", a.mat[0][i]);
            printf("%d", a.mat[0][n - 1]);
            continue;
        }
        b = a * qkpow(b, m);
        for (int i = 0; i < n - 1; i++)printf("%d ", b.mat[0][i]);
        printf("%d\n", b.mat[0][n - 1]);
    }
    return 0;
}

HDU - 4686
a [ i ] ∗ b [ i ] = a [ i − 1 ] ∗ b [ i − 1 ] ∗ A X ∗ B X + A Y ∗ B X ∗ b [ i − 1 ] + B Y ∗ A X ∗ a [ i − 1 ] + A Y ∗ b Y a[i]*b[i]=a[i-1]*b[i-1]*AX*BX+AY*BX*b[i-1]+BY*AX*a[i-1]+AY*bY a[i]b[i]=a[i1]b[i1]AXBX+AYBXb[i1]+BYAXa[i1]+AYbY

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>

using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
ll n;
ll a0, b0, ax, bx, ay, by;

struct Mat {
    ll mat[5][5];

    void init(int x) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < 5; i++)mat[i][i] = x;
    }

    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < 5; i++)
            for (int j = 0; j < 5; j++)
                for (int k = 0; k < 5; k++)
                    c.mat[i][j] = (c.mat[i][j] + mat[i][k] * b.mat[k][j]) % mod;
        return c;
    }
} a, b;

Mat qkpow(Mat a, ll b) {
    Mat sum;
    sum.init(1);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

int main() {
    while (scanf("%lld", &n) == 1) {
        scanf("%lld%lld%lld%lld%lld%lld", &a0, &ax, &ay, &b0, &bx, &by);
        a.init(0);
        b.init(0);
        b.mat[0][0] = ax * bx % mod;
        b.mat[1][0] = ay * bx % mod;
        b.mat[2][0] = by * ax % mod;
        b.mat[4][0] = ay * by % mod;
        b.mat[1][1] = bx % mod;
        b.mat[4][1] = by % mod;
        b.mat[2][2] = ax % mod;
        b.mat[4][2] = ay % mod;
        b.mat[0][3] = b.mat[3][3] = b.mat[4][4] = 1;
        a.mat[0][0] = a0 * b0 % mod;
        a.mat[0][1] = b0 % mod;
        a.mat[0][2] = a0 % mod;
        a.mat[0][4] = 1;
        b = a * qkpow(b, n);
        printf("%d\n", b.mat[0][3]);
    }
    return 0;
}

UVA - 1386

题意:有一个细胞自动机,它是一个由 n 个元素组成的环,每个元素的值都必须是 mod m意义下的。现在你需要对这个环进行 k 次操作,每次操作你需要把这个环内每个元素更新成与它距离不超过 d 的所有元素之和(包括自己)。注:每一个新的元素也必须是 mod m 意义下的。

思路:发现需要构造的矩阵为循环矩阵

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
int n, mod, d, k;
struct Mat {
    ll mat[500];
    Mat operator*(Mat b) {//循环矩阵乘
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                c.mat[i] = (c.mat[i] + mat[(j - i + n) % n] * b.mat[j]) % mod;
        return c;
    }
} a, b;
Mat qkpow(Mat a, ll b) {
    Mat sum;
    memset(sum.mat, 0, sizeof sum.mat);
    sum.mat[0] = 1;
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}
int main() {
    while (scanf("%d%d%d%d", &n, &mod, &d, &k) == 4) {
        for (int i = 0; i < n; i++)scanf("%d", &a.mat[i]);
        memset(b.mat, 0, sizeof b.mat);
        for (int i = 0; i <= d; i++)b.mat[i] = 1;
        for (int i = n - 1; i > n - 1 - d; i--)b.mat[i] = 1;
        b = qkpow(b, k) * a;
        for (int i = 0; i < n - 1; i++)printf("%d ", b.mat[i]);
        printf("%d\n", b.mat[n - 1]);
    }
    return 0;
}

UVA - 11651

题意:有一个 b a s e ( 2 ≤ b a s e ≤ 6 ) base(2≤base≤6) base(2base6)进制系统,这里面的数都是整数,不含前导 0 0 0,相邻两个数字不相同。
而且每个数字有一个得分 s c o r e ( 1 ≤ s c o r e ≤ 109 ) score(1≤score≤109) score(1score109),得分为 相邻两个数字之差的平方之和。
给出 b a s e base base s c o r e score score,求满足条件的整数的个数 m o d mod mod 2 32 2^{32} 232
思路
首先考虑DP的做法:
d p ( i , j ) dp(i,j) dp(i,j)表示满足当前分数为i最后一个数字是j的数字的个数。
递推就是枚举下一个数字 k k k,就有转移方程:
d p ( i + d , k ) = ∑ d p ( i , j ) dp(i+d,k)=∑dp(i,j) dp(i+d,k)=dp(i,j),其中 k ≠ j k≠j k=j d = ( k − j ) 2 d=(k−j)^2 d=(kj)2
这种方法的复杂度使 O ( b a s e 2 ⋅ s c o r e ) O(base^2⋅score) O(base2score)的。

考虑矩阵优化
因为状态转移中,能得到的最大分数是 ( b a s e − 1 ) 2 (base−1)^2 (base1)2,所以我们的转移矩阵只要保留前面 ( b a s e − 1 ) 2 (base−1)^2 (base1)2 s c o r e score score的信息就行了。
用语言不方便表达,我举具体例子,
b a s e = 3 base=3 base=3时,有如下转移:
在这里插入图片描述
上面9行很好理解,就是一个错位。
下面3行才是状态的转移。
容易看出,我们的矩阵的边长最大会达到 6 ∗ ( 6 − 1 ) 2 = 150 6*(6−1)^2=150 6(61)2=150

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
typedef unsigned int ul;
int t, n, m, cas, N;
struct Mat {
    ul mat[150][150];
    void init(ul x, int n) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < n; i++)mat[i][i] = x;
    }
    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < N; i++)
            for (int j = 0; j < N; j++)
                for (int k = 0; k < N; k++)
                    c.mat[i][j] += mat[i][k] * b.mat[k][j];
        return c;
    }
} a, b;
Mat qkpow(Mat a, int b) {
    Mat sum;
    sum.init(1, N);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}
ul dp[25][6];//  i:score    j:last digit
int main() {
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        int nn = (n - 1) * (n - 1);
        N = n * nn;
        b.init(0, N);
        a.init(0, N);
        memset(dp, 0, sizeof dp);
        //  dp
        for (int i = 1; i < n; i++)dp[0][i] = 1;
        for (int i = 0; i < nn; i++)
            for (int j = 0; j < n; j++)
                for (int k = 0; k < n; k++) {
                    int d = (j - k) * (j - k);
                    if (!d || i + d >= nn)continue;
                    dp[i + d][k] += dp[i][j];
                }
        for (int i = 0; i < nn; i++)
            for (int j = 0; j < n; j++)
                a.mat[i * n + j][0] = dp[i][j];
        for (int i = 0; i < N - n; i++)
            b.mat[i][n + i] = 1;
        for (int j = 0; j < n; j++)
            for (int k = 0; k < n; k++) {
                int d = (j - k) * (j - k);
                if (!d || nn - d < 0)continue;
                b.mat[(nn - 1) * n + j][(nn - d) * n + k] = 1;
            }
        b = qkpow(b, m) * a;
        ul res = 0;
        for (int i = 0; i < n; i++)res += b.mat[i][0];
        printf("Case %d: %u\n", ++cas, res);
    }
    return 0;
}

CodeForces - 392C
在这里插入图片描述
在这里插入图片描述

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>

using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;

struct Mat {
    ll mat[83][83];

    void init(ll x) {
        memset(mat, 0, sizeof mat);
        for (int i = 0; i < 83; i++)mat[i][i] = x;
    }

    Mat operator*(Mat b) {
        Mat c;
        memset(c.mat, 0, sizeof c.mat);
        for (int i = 0; i < 83; i++)
            for (int j = 0; j < 83; j++)
                for (int k = 0; k < 83; k++)
                    c.mat[i][j] = (c.mat[i][j] + mat[i][k] * b.mat[k][j]) % mod;
        return c;
    }
} a, b;

Mat qkpow(Mat a, ll b) {
    Mat sum;
    sum.init(1);
    while (b) {
        if (b & 1)sum = sum * a;
        a = a * a;
        b >>= 1;
    }
    return sum;
}

ll n, k;
ll c[50][50];

void init() {
    for (int i = 0; i <= 40; i++)
        for (int j = 0; j <= i; j++)
            if (!j)c[i][j] = 1;
            else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}

int main() {
    cin >> n >> k;
    init();
    b.init(0);
    b.mat[0][0] = 1;
    for (int i = 0; i <= k; i++)b.mat[0][i + 1] = c[k][i];
    for (int i = 0; i <= k; i++)b.mat[0][k + i + 2] = c[k][i];
    for (int i = 1; i <= k + 1; i++) {
        for (int j = 1; j <= i; j++)b.mat[i][j] = c[i - 1][j - 1];
        for (int j = 1; j <= i; j++)b.mat[i][j + k + 1] = c[i - 1][j - 1];
    }
    for (int i = 1; i <= k + 1; i++)
        for (int j = 1; j <= i; j++)
            b.mat[i + k + 1][j] = c[i - 1][j - 1];
    b = qkpow(b, n - 1);
    int res = 0;
    for (int i = 0; i < 2 * k + 3; i++)res = (res + b.mat[0][i]) % mod;
    printf("%d\n", res);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值