BZOJ-1875 HH去散步 DP+矩阵乘法快速幂

1875: [SDOI2009]HH去散步
Time Limit: 20 Sec Memory Limit: 64 MB
Submit: 1196 Solved: 553
[Submit][Status][Discuss]

Description
HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。 现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径

Input
第一行:五个整数N,M,t,A,B。其中N表示学校里的路口的个数,M表示学校里的 路的条数,t表示HH想要散步的距离,A表示散步的出发点,而B则表示散步的终点。 接下来M行,每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。数据保证Ai = Bi,但 不保证任意两个路口之间至多只有一条路相连接。 路口编号从0到N − 1。 同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。 答案模45989。

Output
一行,表示答案。

Sample Input
4 5 3 0 0
0 1
0 2
0 3
2 1
3 2

Sample Output
4

HINT
对于30%的数据,N ≤ 4,M ≤ 10,t ≤ 10。 对于100%的数据,N ≤ 20,M ≤ 60,t ≤ 2^30,0 ≤ A,B

Source
Day1

其实这题应该不难,但是真的没想到正解....

题解:
正常是对点构造矩阵,那么这里用边来构造,保证走的时候不经过反向边即可,然后矩乘快速幂加速

code:

#include
#include
#include
#include
#include
using namespace std;
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;
}
#define maxn 25
#define maxm 70
#define p 45989
int n,m,t,st,ed;
struct data{int to,next;}edge[maxm*2];
int head[maxm],cnt;
int l,ans;
struct Mat{int a[maxm*2][maxm*2];Mat(){memset(a,0,sizeof(a));}};

void add(int u,int v)
{
    cnt++;
    edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;
}

Mat mul(Mat A,Mat B)
{
    Mat re;
    for (int i=1; i<=l; i++)
        for (int j=1; j<=l; j++)
            for (int k=1; k<=l; k++)
                re.a[i][j]=(re.a[i][j]+(A.a[i][k]*B.a[k][j])%p)%p;
    return re;              
}

Mat quick_mul(Mat A,int x)
{
    Mat re;
    for (int i=1; i<=l; i++) re.a[i][i]=1;
    while (x)
        {
            if (x&1) re=mul(re,A);
            x>>=1; A=mul(A,A);
        }
    return re;
}

int findre(int x)
{
    if (x&1) return x+1;
        else return x-1;
}

int main()
{
    n=read(),m=read(),t=read(),st=read(),ed=read();
    for (int i=1; i<=m; i++)
        {
            int u=read(),v=read();
            add(u,v); add(v,u);
        }
    Mat x,y;
    for (int i=head[st]; i; i=edge[i].next)
        x.a[1][i]=1;
    l=cnt;  
    if (t==0) {if (st==ed) puts("1"); else puts("0"); return 0;}
    for (int i=1; i<=l; i++)
        for (int j=head[edge[i].to]; j; j=edge[j].next)
            if (j!=findre(i)) y.a[i][j]=1;
    x=mul(x,quick_mul(y,t-1));                          
    for (int i=head[ed]; i; i=edge[i].next)
        ans=(ans+x.a[1][findre(i)])%p;
    printf("%d\n",ans);     
    return 0;
}

你可能感兴趣的:(DP及优化算法,BZOJ,矩阵乘法)