[bzoj1297][SCOI2009]迷路

给定一个n个点的有向图,给定邻接矩阵,每条边的距离都是1-9,求从1号点走到n号点且距离恰好为T的方案数量%2009.   n<=10,T<=10^9

题解:看到T的范围很容易想到矩阵乘法,但是距离不全是1没法搞,所以我们可以把每条边拆点,这样就保证了边权都是1,可以矩阵乘法啦

但是这样的话点的数量有点多,最多n^2*9 ,也就是900个,直接搞还是T。

但是我们发现我们没必要把每条边拿出来拆,只要对每个点拆出9个点,表示离这个点距离为0-8的点,就可以啦,这样点的数量V=n*9

复杂度V^3*logT

#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 2009 
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int n,T;
int f[15][15][35];
char st[15];
int id[15][15],tot=0;

struct sq{
    int f[95][95];
    int n,m;
    sq operator *(sq y)
    {
        sq c;memset(c.f,0,sizeof(c.f));
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                for(int k=1;k<=y.m;k++)
                    c.f[i][j]=(c.f[i][j]+f[i][k]*y.f[k][j])%mod;
        c.n=n;c.m=y.m;
        return c;
    }
}s,s2;

int main()
{
    n=read();T=read(); 
    for(int i=1;i<=n;i++)
        for(int j=0;j<=8;j++)
        {
            id[i][j]=++tot;
            if(j)s.f[tot][tot-1]=1;    
        }
    s.n=s.m=tot;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",st+1);
        for(int j=1;j<=n;j++)
            if(st[j]!='0')
                s.f[id[i][0]][id[j][st[j]-'1']]=1;
    }    
    s2.n=1;s2.m=tot;
    s2.f[1][id[1][0]]=1;
    for(;T;T>>=1,s=s*s)
        if(T&1) s2=s2*s;
    cout<<s2.f[1][id[n][0]];
    return 0;
}
原文地址:https://www.cnblogs.com/FallDream/p/bzoj1297.html