洛谷题单-【图论】省选图论
链接: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; }
看完题解后,发现了更为简单的方法,定义$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); }