Teleportation
鮑勃最近訪問了一個奇怪的傳送系統。該系統包含 \(n\) 個房間,編號為 \(0\) 到 \(n-1\)。每個房間都安裝了一個傳送裝置。每個傳送裝置都有一個看起來像鐘錶表面的儀表板,上面有一個指標,顯示數字 \(0\) 到 \(n-1\),按順時針順序排列。最初,第 \(i\) 個房間的傳送裝置上的指標指向數字 \(a_i\)。
當鮑勃在房間 \(i\)(\(0 \leq i \leq n-1\))時,他可以進行以下操作任意次數:
- 傳送:立即傳送到房間 \((i + a_i) \mod n\)。
- 逆時針移動指標。設定 \(a_i \leftarrow a_i + 1\)。
每次操作需要一個時間單位。鮑勃從房間 \(0\) 開始,他想盡快到達某個房間 \(x\)。他想知道需要多長時間。
\(Input\)
輸入的第一行包含兩個整數 \(n\)(\(2 \leq n \leq 10^5\))和 \(x\)(\(1 \leq x \leq n - 1\)),分別表示房間的數量和鮑勃的目的地房間。
下一行包含 \(n\) 個整數 \(a_0, a_1, \ldots, a_{n-1}\)(\(0 \leq a_i \leq n - 1\)),其中 \(a_i\)(\(0 \leq i \leq n - 1\))表示第 \(i\) 個房間的指標指向的數字。
\(Output\)
輸出一個整數,表示鮑勃從房間 0 到達房間 \(x\) 所需的最短時間。
\(Sample1\)
4 3
0 1 2 3
4
\(Sample2\)
4 3
0 0 0 0
4
\(Sample3\)
4 3
2 2 2 2
2
思路:這題得轉換一下,不然邊要建太多了,不妨設1到n-1為本,n到\(2*n-1\)為真,對於本來講,從1到n-1做有向邊,從左指向右邊(注意n-1->0也要),設立邊費用為1;且對於每個數建立個零費用邊到其對應的真邊
隨後就是i+a[i]了,建立對應i的真邊i+n到(i+a[i])%n的本的單向邊,且設立費用為1.
隨後優先佇列走一個最短路就行了,從n出發
從真點集出發一是可以直接到對應連線的本點,二是可以利用本點集中單向邊的指向性質,一個有向環,實現費用轉移最佳化。
這樣子既合理有實現了邊集大小的控制,即3*n,最後走一遍迪傑斯特拉就可得到答案
由於迪傑斯特拉本質就是最近擴散,所以當遇以目標點擴散時,其一定為最小值,這時可以直接break
#include<iostream>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#include<cmath>
#include<string>
#define ll long long
#define lowbit(x) (x & -x)
#define endl "\n"
using namespace std;
vector<pair<ll,ll>>g[250000];
void fio()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
}
bool vis[250000];
ll n,m;
ll ans=0;
void bfs()
{
priority_queue<pair<ll,ll>,vector<pair<ll,ll>>,greater<pair<ll,ll>>>q;
q.push({0,n});
while(!q.empty())
{
ll x=q.top().first;
ll y=q.top().second;
q.pop();
if(vis[y])
continue;
vis[y]=1;
if(y==m+n)
{
ans=x;
break;
}
for(auto j:g[y])
{
q.push({x+j.first,j.second});
}
}
}
int main()
{
fio();
cin>>n>>m;
for(ll i=0;i<n;i++)
{
ll x;
cin>>x;
g[n+i].push_back({1,(x+i)%n});
}
for(ll i=0;i<n-1;i++)
{
g[i].push_back({1,i+1});
g[i].push_back({0,i+n});
}
g[n-1].push_back({1,0});
g[n-1].push_back({0,2*n-1});
bfs();
cout<<ans<<endl;
}