C++圖論與矩陣加速—————[SCOI2009]迷路(道路千萬條)

題目描述:

道路千萬條,安全第一條!宏帆校區到渝北校區有很多種走法,我們可以把走法看成N個節點的有向圖,假設宏帆代表0號節點,渝北代表N-1號節點,GM想從0號節點出發,到N-1號節點,但必須恰好在T時刻到達!你能告訴GM一共有多少種走法嗎?注意:GM不能在某個節點逗留,且通過某有向邊時嚴格爲給定時間(邊權)。

輸入:

第一行包含兩個整數,N T。 接下來有 N 行,每行一個長度爲 N 的字符串。 第i行第j列爲'0'表示從節點i到節點j沒有邊。 爲'1'到'9'表示從節點i到節點j需要耗費的時間。

滿足 2 <= N <= 10 ; 1 <= T <= 1000000000。

輸出:

包含一個整數,可能的路徑數,這個數可能很大,只需輸出這個數除以2009的餘數。

輸入樣例1:

2 2
11
00

輸出樣例1:

1

輸入樣例2:

5 30

12045

07105

47805

12024

12345

輸出樣例2:

852

思路分析:

我們先這樣弱化一下題目

假設這個方陣只有0或1,那麼其就是不是表示它們點之間的有向邊。

而它自己和自己相乘就是表示從一個頂點到另一個頂點。

在T次方之後,矩陣的數就是從i到j的方案數。(其中i爲行數,j爲列數)。

遺憾的是本題的邊權是[0,9],不是[0,1]。

驚喜的是最大邊權是9!N也不超過10!都不算大!

馬上想到:拆點!把一個點拆成9個點,max:10*9。(當時我連拆點是什麼都不知道)。

拆點:

假設有兩個節點,邊權不超過2的2階方陣

可以畫圖爲:

由於n和邊權都不大,所以想到拆點,把原先的1個點拆成(max邊權)個點,所以拆完之後仍然是一個n*(max邊權)的方陣

我們先將單鏈連接起來,畢竟同點。

然後由於一個規則,原圖的i,j點是現圖的i.1和j.1點,所以它們的邊就是從i.1到j.1的路了。

那麼原i到j的路程就是先從i.1到i.邊權-1到2.1了。

具體拆分如下:

我們把1.1看成節點1;把1.2看成節點2;把2.1看成節點3;把2.2看成節點4;可得到新矩陣 :

這樣我們就可以求出答案。

而新矩陣的行列就是N=n*maxn(maxn爲最大邊)。

最後一點,答案爲B[1][N-maxn+1],這一點就自行思考(實在寫不下去了)。

代碼實現;

#include<cstdio>
#include<iostream>
#include<cstring>
#define mod 2009
using namespace std;
int n,m,t,maxn;
char a[15][15];
struct node{
    int n,m,c[105][105];
    node ()
    {
        memset(c,0,sizeof(c));
    }
    node operator*(const node &a)
    {
        node r;
        r.n=n;
        r.m=a.m;
        for(int i=1;i<=r.n;i++)
            for(int j=1;j<=r.m;j++)
                for(int k=1;k<=m;k++)
                    r.c[i][j]=(r.c[i][j]+(c[i][k]*a.c[k][j])%mod)%mod;
        return r;
    }
};
node qkpow(node a,int p)
{
    node res;
    res.n=res.m=n;
    for(int i=1;i<=res.n;i++)
        res.c[i][i]=1;
    while(p)
    {
        if(p&1)
            res=res*a;
        a=a*a;
        p/=2;
    }
    return res;
}
int js(int x,int y)
{
    return (x-1)*maxn+y;
}
int main()
{
    scanf("%d%d",&n,&t);
    for(int i=1;i<=n;i++)
    {
        scanf("\n");
        for(int j=1;j<=n;j++)
        {
            scanf("%c",&a[i][j]);
            maxn=max(a[i][j]-'0',maxn);
        }
    }
    node b;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<maxn;j++)
            b.c[js(i,j)][js(i,j+1)]=1;
        for(int j=1;j<=n;j++)
            if(a[i][j]!='0')
                b.c[js(i,a[i][j]-'0')][js(j,1)]=1;
    }
    n*=maxn;
    b.n=n;
    b.m=n;
    b=qkpow(b,t);
    printf("%d",b.c[1][n-maxn+1]);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章