BZOJ 1875 [SDOI2009]HH去散步 矩陣乘法

題意:
給定一張無向圖,每條路的長度都是1,沒有自環,可能有重邊,給定起點與終點,求從起點走t步到達終點的方案數。
每一步走的時候要求不能走上一條剛剛走的路。
解析:
顯然需要搞出個矩陣之後矩乘。
然而這題的要求就很煩,要不然就是SB題了2333.
但是我們可以換一個想法來做。
題目要求不走上一條來的邊,況且邊的數量還很少,所以我們可以考慮將矩陣構造成從一條邊走到另一條邊的方案數。
這樣的話我們避免走上一條來的路就很簡單的能判斷了。
構造出初始矩陣後,我們求該矩陣的t-1次方。
然後再用起點走一步能到的邊的矩陣乘以該矩陣。
之後起點到鏈接終點的邊的所有方案數之和即爲答案。
代碼:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define mod 45989
#define N 22
using namespace std;
int n,m,t,a,b;
struct Matrix
{
    int map[N*6][N*6];
    void clear(){memset(map,0,sizeof(map));}
    friend Matrix operator * (Matrix &a,Matrix &b)
    {
        Matrix ret;
        for(int i=0;i<=2*m;i++)
        {
            for(int j=0;j<=2*m;j++)
            {
                ret.map[i][j]=0;
                for(int k=0;k<=2*m;k++)
                {                   
                    ret.map[i][j]=ret.map[i][j]+a.map[i][k]*b.map[k][j]%mod;
                }
                ret.map[i][j]%=mod;
            }
        }
        return ret;
    }
    friend Matrix operator ^ (Matrix &a,int b)
    {
        Matrix ret;
        ret.clear();
        for(int i=0;i<=m*2;i++)ret.map[i][i]=1;
        while(b)
        {
            if(b&1)ret=ret*a;
            a=a*a;
            b>>=1;
        }
        return ret;
    }
}ori,base;
int head[N],cnt;
struct node
{
    int from,to,next;
}edge[N*6];
void init()
{
    memset(head,-1,sizeof(head));
    cnt=1;
}
void edgeadd(int from,int to)
{
    edge[cnt].from=from,edge[cnt].to=to,edge[cnt].next=head[from];
    head[from]=cnt++;
}
int main()
{
    init();
    scanf("%d%d%d%d%d",&n,&m,&t,&a,&b);
    a++,b++;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        x++,y++;
        edgeadd(x,y),edgeadd(y,x);
    }
    for(int i=head[a];i!=-1;i=edge[i].next)
        base.map[0][i]++;
    for(int i=1;i<cnt;i++)
    {
        int x=edge[i].from,y=edge[i].to;
        for(int j=head[y];j!=-1;j=edge[j].next)
        {
            if(j!=(i+((i&1)?1:-1)))
                ori.map[i][j]++;
        }
    }
    ori=ori^(t-1);
    base=base*ori;
    int sum=0;
    for(int i=1;i<cnt;i++)
    {
        if(edge[i].to==b)
            sum=(sum+base.map[0][i])%mod;
    }
    printf("%d\n",sum);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章