https://ac.nowcoder.com/acm/contest/879/A
完全图中从 点 1 1 1 出发,经过 k k k 条边(可重复)回到点 1 1 1 的方案数。
设矩阵 A A A 中每个元素都为 1 1 1,矩阵 I I I 为单位矩阵,
所以根据题意,我们要求的就是 ( A − I ) 0 , 0 k (A-I)^k_{0,0} (A−I)0,0k。
根据矩阵二项式定理: ( A − I ) k = ∑ i = 0 k ( k i ) A i ( − 1 ) k − i (A-I)^k=\sum_{i=0}^{k}\binom{k}{i}A^i(-1)^{k-i} (A−I)k=∑i=0k(ik)Ai(−1)k−i。
所以
( A − I ) 0 , 0 k = ∑ i = 0 k ( k i ) n max ( 0 , i − 1 ) ( − 1 ) k − i = ∑ i = 0 k ( k i ) n max ( 1 , i ) n ( − 1 ) k − i = 1 n ( ( n − 1 ) k − ( k 0 ) n 0 ( − 1 ) k + ( k 0 ) n 1 ( − 1 ) k ) = ( n − 1 ) k + ( − 1 ) k ( n − 1 ) n \begin{aligned} (A-I)^k_{0,0} &=\sum_{i=0}^{k}\binom{k}{i}n^{\max(0,i-1)}(-1)^{k-i}\\ &=\sum_{i=0}^{k}\binom{k}{i}\frac{n^{\max(1,i)}}{n}(-1)^{k-i}\\ &=\frac{1}{n}((n-1)^k-\binom{k}{0}n^{0}(-1)^{k}+\binom{k}{0}n^{1}(-1)^{k})\\ &=\frac{(n-1)^k+(-1)^k(n-1)}{n}\\ \end{aligned} (A−I)0,0k=i=0∑k(ik)nmax(0,i−1)(−1)k−i=i=0∑k(ik)nnmax(1,i)(−1)k−i=n1((n−1)k−(0k)n0(−1)k+(0k)n1(−1)k)=n(n−1)k+(−1)k(n−1)
#include
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
//head
const int MOD=998244353;
LL n,k;
LL qpow(LL a,LL b) {
LL ret=1;
while(b) {
if(b&1) ret=ret*a%MOD;
a=a*a%MOD;
b>>=1;
}
return ret;
}
int main() {
//freopen("E:/OneDrive/IO/in.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n>>k;
cout<<(qpow(n-1,k)+(n-1)*(k&1?-1:1)+MOD)%MOD*qpow(n,MOD-2)%MOD<<'\n';
return 0;
}
记 d p i , 1 dp_{i,1} dpi,1 为经过 i i i 条边在点 1 1 1 的方案数, d p i , 0 dp_{i,0} dpi,0 为经过 i i i 条边不在点 1 1 1 的方案数。
转移方程:
{ d p [ i ] [ 1 ] = d p [ i − 1 ] [ 0 ] d p [ i ] [ 0 ] = ( n − 1 ) d p [ i − 1 ] [ 1 ] + ( n − 2 ) d p [ i − 1 ] [ 0 ] \begin{cases} dp[i][1]=dp[i-1][0]\\ dp[i][0]=(n-1)dp[i-1][1]+(n-2)dp[i-1][0] \end{cases} {dp[i][1]=dp[i−1][0]dp[i][0]=(n−1)dp[i−1][1]+(n−2)dp[i−1][0]
构造矩阵加速递推。
#include
#define SZ(x) (int)(x).size()
#define ALL(x) (x).begin(),(x).end()
#define PB push_back
#define EB emplace_back
#define MP make_pair
#define FI first
#define SE second
using namespace std;
typedef double DB;
typedef long double LD;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
typedef vector<int> VI;
typedef vector<PII> VPII;
//head
const LL MOD=998244353;
int sz;
struct M {
LL a[105][105];
void clear() {memset(a,0,sizeof a);}
M() {clear();}
void init() {
clear();
for(int i=0;i<sz;i++) a[i][i]=1;
}
M operator + (const M &T) const {
M ret;
for(int i=0;i<sz;i++)
for(int j=0;j<sz;j++)
ret.a[i][j]=(a[i][j]+T.a[i][j])%MOD;
return ret;
}
M operator - (const M &T) const {
M ret;
for(int i=0;i<sz;i++)
for(int j=0;j<sz;j++)
ret.a[i][j]=(a[i][j]-T.a[i][j]+MOD)%MOD;
return ret;
}
M operator * (const LL &v) const {
M ret;
for(int i=0;i<sz;i++)
for(int j=0;j<sz;j++) {
if(!a[i][j]) continue;
ret.a[i][j]=a[i][j]*v%MOD;
}
return ret;
}
M operator * (const M &T) const {
M ret;
for(int i=0;i<sz;i++)
for(int k=0;k<sz;k++) {
LL t=a[i][k];
if(!t) continue;
for(int j=0;j<sz;j++) {
if(!T.a[k][j]) continue;
ret.a[i][j]=(ret.a[i][j]+T.a[k][j]*t%MOD)%MOD;
}
}
return ret;
}
M operator ^ (LL b) const {
M ret,bas;
for(int i=0;i<sz;i++) ret.a[i][i]=1;
for(int i=0;i<sz;i++)
for(int j=0;j<sz;j++)
bas.a[i][j]=a[i][j];
while(b) {
if(b&1) ret=ret*bas;
bas=bas*bas;
b>>=1;
}
return ret;
}
void print() {
for(int i=0;i<sz;i++)
for(int j=0;j<sz;j++)
cout<<a[i][j]<<" \n"[j==sz-1];
}
};
int main() {
//freopen("E:/OneDrive/IO/in.txt","r",stdin);
ios::sync_with_stdio(false);
cin.tie(nullptr);
LL n,k;
cin>>n>>k;
sz=2;
M t1,t2;
t1.a[0][0]=1;
t2.a[1][0]=1,t2.a[0][1]=n-1,t2.a[1][1]=n-2;
t1*=t2^k;
cout<<t1.a[0][0]<<'\n';
return 0;
}