題意
給出一個 \(n\) 個點的有向圖,點 \(i\) 連向點 \((i+1)\),點 \(n\) 連向點 \(1\)。再給你 \(m\) 條額外邊。你的初始位置為 \(1\),問你移動 \(k\) 步的不同方案數(僅當路徑不同時兩個方案不同)。
思路
先想怎樣暴力轉移,顯然移動 \(k\) 步到達一個點的方案數為所有跟這個點連邊的移動 \((k-1)\) 步到達的點的方案數的總和,時間複雜度 \(O((n+m)k)\),顯然不能接受。
可以發現 \(m\le 50\),考慮最佳化掉 \(n\)。最開始的 \(n\) 條初始邊構成了一個環,所以每個點 \(i\) 都一定會從 \((i-1)\) 轉移過來,也就是環上的數整體右移一位。我們換位思考,不去右移環上的數,而是整體左移一位額外邊上的數,時間複雜度就會大大減小了。因為最外圈是個環,所以整體左移額外邊雖然會改變連邊關係,但是不會改變邊的相對位置,對答案不會造成影響。時間複雜度 \(O(mk)\),可以透過。
程式碼
#include<bits/stdc++.h>
#define md 998244353
using namespace std;
int n,m,k,num[200005],ANS;
vector<int> t[200005],pt;
int to(int x,int i){//將點x左移i位,注意取模
return ((x-i-1)%n+n)%n+1;
}
signed main(){
cin>>n>>m>>k;
num[1]=1;
for(int x,y,i=1;i<=m;i++){
cin>>x>>y;
if(!t[x].size())
pt.push_back(x);
t[x].push_back(y);
}
for(int i=0;i<k;i++){//這裡先從額外邊轉移再整體左移,所以i從0開始
vector<pair<int,int>> cge;
for(int v:pt)
for(int v2:t[v])
cge.push_back({num[to(v,i)],to(v2,i+1)});//因為整體左移,所以從原來的u->v變為了u->(v-1)
for(pair<int,int> v:cge)
num[v.second]=(1ll*num[v.second]+v.first)%md;
}
for(int i=1;i<=n;i++)
ANS=(ANS+num[i])%md;
cout<<ANS;
return 0;
}