bzoj 1875 [SDOI2009]HH去散步

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1875

正常套路的本质是dp[ i ]。现在有了对边的限制,就把状态改成dp[ i ][ j ]表示从 j 这条边来 i 的方案数。

空间有点多。考虑每条边不是对应n个点,只对应两个点,所以只记录dp[ j ]表示边的走过次数,把端点记在边的结构体里。

然后xnt从1开始就很好弄了。

第39和46行用邻接表而不是枚举所有边 + 定义 * 里少模一下mod 可以让3000ms+变成1800ms+。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=25,M=65,mod=45989;
int n,m,t,st,ed,xnt=1,prn,head[N];
struct Edge{
    int next,from,to;
    Edge(int n=0,int f=0,int t=0):next(n),from(f),to(t) {}
}edge[M<<1];
struct Matrix{
    int a[M<<1][M<<1];
    Matrix(){memset(a,0,sizeof a);}
    Matrix operator*(const Matrix &b)const
    {
        Matrix c;
        for(int i=1;i<=xnt;i++)//xnt
            for(int k=1;k<=xnt;k++)
                for(int j=1;j<=xnt;j++)
                    (c.a[i][j]+=a[i][k]*b.a[k][j])%=mod;//////
        return c;
    }
}r,ans;
void add(int x,int y)
{
    edge[++xnt]=Edge(head[x],x,y);head[x]=xnt;
    edge[++xnt]=Edge(head[y],y,x);head[y]=xnt;
}
int main()
{
    scanf("%d%d%d%d%d",&n,&m,&t,&st,&ed);
    int x,y;
    for(int i=1;i<=m;i++)
        scanf("%d%d",&x,&y),add(x,y);
    for(int i=2;i<=xnt;i++)
        for(int j=2;j<=xnt;j++)//j=2
            if(edge[i].to==edge[j].from&&i!=(j^1))//则j能从i转移来
                 r.a[i][j]=1;//第j列:在求状态的第j个;第i行:加上状态的第i列 
    for(int i=head[st];i;i=edge[i].next)ans.a[1][i]=1;
    t--;
    while(t)
    {
        if(t&1)ans=ans*r;
        r=r*r;t>>=1;
    }
    for(int i=head[ed];i;i=edge[i].next)(prn+=ans.a[1][i^1])%=mod;
    printf("%d",prn);
    return 0;
}

 

posted on 2018-06-30 15:50  Narh  阅读(91)  评论(0编辑  收藏  举报

导航