hdu 5411 2015多校十1006 ~矩陣快速冪

題意:

給定n個碎片的轉移關係,問最多使用m個碎片組成的不同的序列個數是多少。

思路:

容易想到dp的方法,以dp[i][j]表示長度爲i以j號碎片結尾的不同序列的數量。那麼dp[i + 1][k] += dp[i][j](j -> k 可以轉移)dp[0][0] = 1;最後的答案即爲整個dp[i][j]數組所有元素的和。但是一算複雜度,超時。
於是乎想到矩陣,關係矩陣A的k次方可以求出任意長度爲k+1的序列總的數量。但是這道題要求最多長度爲m的總數量,也就是說要求A^0 + A^1 + A^2… + A^m - 1。如果分別用快速冪,算一算複雜度依舊超時。然後就發現了構造矩陣的神奇方法。構造的矩陣B如下:
A . . . A 1
A . . . A 1
. .. . …. A 1
. . . . A 1
A . . . A 1
0 . . . 0 1
也就是在原來的轉移矩陣最右邊加上一列1,用於保存上一步以及之前的總的答案,
最後的答案爲矩陣B^(m-1) 的所有項的和。時間複雜度O(n^3 *log(m)),注意特判m = 1(論構造矩陣的神奇)

附搓代碼:

#include <cstdio>
#include <cstring>
using namespace std;
const int piece = 55;
int ch[piece][piece];
int ans[piece][piece];
int tp[piece][piece];
int n, m;
void cal(int a[][piece], int b[][piece], int c[][piece])
{
    for(int i = 1; i <= n + 1; i++)
        for(int j = 1; j <= n + 1; j++)
        {
            int tmp = 0;
            for(int k = 1; k <= n + 1; k++)
                tmp = (tmp + a[i][k] * b[k][j]) % 2015;
            tp[i][j] = tmp;
        }
    memcpy(c, tp, sizeof(tp));
}
void rapid(int a[][piece], int k, int b[][piece])
{
    for(int i = 1; i <= n + 1; i++) b[i][i] = 1;
    while(k)
    {
        if(k & 1)
            cal(b, a, b);
        k >>= 1;
        cal(a, a, a);
    }
}
int main()
{
    // freopen("5411.in","r",stdin);
    // freopen("5411out.txt","w",stdout);
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d", &n, &m);
        memset(ch, 0, sizeof(ch));

        for(int i = 1; i <= n; i++)
        {
            int k;
            scanf("%d", &k);
            for(int j = 0; j < k; j++)
            {
                int tmp;
                scanf("%d", &tmp);
                ch[i][tmp] = 1;
            }
        }
        if(m == 1)
            printf("%d\n", n + 1);
        else
        {
            memset(ans, 0, sizeof(ans));
            for(int i = 1; i <= n + 1; i ++)
                ch[i][n + 1] = 1;
            for(int i = 1 ; i <= n; i++)
                ch[n + 1][i] = 0;
            rapid(ch, m - 1, ans);
            int ls = 0;
            for(int i = 1; i <= n + 1; i++)
                for(int j = 1; j <= n + 1; j++)
                    ls = (ls + ans[i][j]) % 2015;
            printf("%d\n", ls);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章