洛谷题单-【图论】省选图论

链接:https://www.luogu.com.cn/training/2965#information

 

 

P3758 [TJOI2017]可乐(图论DP)

题意:

给一张无向图,先在从点$1$出发,每次可以有$3$种选择,可以原地不动,可以选择爆炸(即直接结束),还可以选择向其他点移动,问总的移动方案数

思路:

很有意思的一道图论$DP$,自己做的时候用$2e8$的复杂度开着$O2$卡过去了,没开的话只能过最后两个点

最开始想的是个搜索的方法,直接给$T$傻了,随便一测当$t=30$的时候就出不来了

之后就想了个$DP$,定义$DP[i][j]$为经过前$i$秒到$j$的方案数,但是这样会漏掉自己爆炸的方案,正确的做法应该是一个新的点$n=0$,就代表着爆炸,并将所有点连向它

转态转移方程也很简单$DP[i][j]=\sum dp[i-1][j],j$为与$i$连接的点,注意还有自己也可以连向自己

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstdio>
typedef long long ll;
 using namespace std;
 const int maxn=1e6+10;
 const int mod=2017;
 vector<int> a[31];
 ll n,m,t,ans=0,dp[31][2],pos,last;
 int main()
 {
     scanf("%lld%lld",&n,&m);
     for(int i=1;i<=m;i++){
         int u,v;
         scanf("%d%d",&u,&v);
         a[u].push_back(v);
         a[v].push_back(u);
     }
    scanf("%lld",&t);
    dp[1][0]=1;
    pos=1;
    for(int i=1;i<=t;i++){
        last=pos^1;
        for(int j=1;j<=n;j++) dp[j][pos]=0;
        for(int j=1;j<=n;j++){
            ans=(ans+dp[j][last])%mod;
            dp[j][pos]=(dp[j][pos]+dp[j][last])%mod;
            for(int k=0;k<a[j].size();k++){
                int v=a[j][k];
                dp[j][pos]=(dp[j][pos]+dp[v][last])%mod;
            }
            if(i==t){
                ans=(ans+dp[j][pos])%mod;
            } 
        }
        pos^=1;
    }
    printf("%lld",ans%mod);
    return 0;
  } 
View Code

看完题解后,发现了更为简单的方法,定义$A$为图的邻接矩阵,那么$A^{k}$的矩阵中第$i$行第$j$列的意义就为经过$k$秒后从$i$到$j$的方案数

爆炸与原地不动的处理方法与我之前说的基本一致,就是新建一个节点与连上自环即可,最后答案就为$\sum_{i=0}^{n} A[1][i]$

至于为什么邻接矩阵有这个含义,我们可以看看矩阵乘法的含义,相乘过后的矩阵$A[i][j]=\sum_{k=1}^{n}A[i][k]*A[k][j]$可以认为$k$为中间点,从$i$到$j$的方案数就为从$i$到不同中间节点再到$j$的方案数的总和,这样就能很好的解释了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<stack>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int P=2017;
struct Matrix{
    int a[31][31];
    inline Matrix operator * (const Matrix &rhs)
    {
        Matrix ret;
        memset(&ret,0,sizeof ret);
        for(int i=0;i<=30;i++)
            for(int j=0;j<=30;j++)
                for(int k=0;k<=30;k++)
                    (ret.a[i][j]+=a[i][k]*rhs.a[k][j]%P)%=P;
        return ret;
    }
}mp;
Matrix ksm(Matrix &a,int b)
{
    Matrix ret;
    memset(&ret,0,sizeof ret);
    for(int i=0;i<=30;i++) ret.a[i][i]=1;
    while(b)
    {
        if(b&1) ret=ret*a;
        a=a*a;b>>=1;
    }
    return ret;
}
int u,v,n,m,t;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u,&v);
        mp.a[u][v]=1;mp.a[v][u]=1;
    }
    for(int i=0;i<=n;i++)
        mp.a[i][i]=1;
    for(int i=1;i<=n;i++) mp.a[i][0]=1;
    int ans=0;
    scanf("%d",&t);
    Matrix anss=ksm(mp,t);
    for(int i=0;i<=n;i++) (ans+=anss.a[1][i])%=P;
    printf("%d\n",ans);
}
View Code

 

posted @ 2020-04-18 16:30  overrate_wsj  阅读(277)  评论(0编辑  收藏  举报