[CF702E]Paired Payment
壹、题目描述 ¶
中文大意:
给定图 \(G=\lang V,E\rang\)(不一定保证 \(G\) 是一个连通图),请你找到从 \(1\) 开始到达任意一个点 \(t(t\in [1,n])\) 的最短路长度。
但是每次你不能只走 \(E\) 中的一条边,而是选择两条边 \(e_1,e_2\in E\;\text{s.t.}\;e_1=\lang x,u,w_1\rang,e_2=\lang u,y,w_2\rang\),然后,执行 \(x\overset{e_1}\rightarrow u\overset{e_2}\rightarrow y\) 且每次走的花费是 \((w_1+w_2)^2\).
保证 \(|V|\le 10^5,|E|\le \min\left\{2\times 10^5,{|V|\times (|V|-1)\over 2}\right\},\forall e=\lang u,v,w\rang \in E,1\le w\le 50\and u\neq v\).
贰、题解 ¶
§ Hint1 §
你真的看到 \(\forall e=\lang u,v,w\rang \in E,w\le 50\) 了吗?
§ Hint2 §
对于一个中间节点,它前一步走的边的权值会对下一步造成影响。
正是因为这个影响,让我们无法跑 \(\tt dijkstra\),那么如何处理这个影响才能让 \(\tt dijkstra\) 继续工作?
§ Hint3 §
边的权值最大只有 \(50\),我们是否可以记录走到一个节点时它前一步边的权值为多少,让不同情况分开计算,使得我们的 \(\tt dijkstra\) 能够继续工作?
§ 正解 §
由于我们是一次走两条边,对于 \(x\rightarrow u\rightarrow y\) 的中间节点 \(u\),我们实际上需要关心的是它前一步走的边的权值是多少,不难发现,对于一个点,它前一步边的权值最多只有 \(50\) 种,所以对于一个点 \(u\),我们可以记录它前一步走的边的权值 \(i(i\in [0,50])\),可以考虑开一个 \(\tt dis[u][i]\) 表示走到点 \(u\),前一步的边权值为 \(i\) 进行记录,但是为什么 \(i\) 可以取 \(0\)?这是为了算法的普遍性,\(i=0\) 表示的并不是点 \(u\) 是中间点,而是结束点,这样,我们可以通过询问 \(\tt dis[u][0]\) 是否为 \(+\infty\) 来判断是否存在路径从 \(1\) 到 \(u\).
然后,直接在这个图上面跑 \(\tt dijkstra\) 就可以了,时间复杂度 \(\mathcal O(|E|\times \max W\times \log |E|)\),空间复杂度 \(\mathcal O(|V|\max W)\),其中 \(W\) 表示边权。
叁、参考代码 ¶
const int maxn=1e5;
const int maxm=2e5;
const ll inf=1ll<<60;
vector<pii>g[maxn+5];
int n, m;
inline void input(){
n=readin(1), m=readin(1);
int u, v, w;
rep(i, 1, m){
u=readin(1), v=readin(1), w=readin(1);
g[u].push_back(mp(v, w));
g[v].push_back(mp(u, w));
}
}
struct node{
int u, pre; ll d;
node(){}
node(int U, int P, ll D): u(U), pre(P), d(D){}
inline int operator <(const node rhs) const{
return d>rhs.d;
}
};
priority_queue<node>Q;
ll dis[maxn+5][55];
inline void dijkstra(){
rep(i, 1, n) rep(j, 0, 50) dis[i][j]=inf;
Q.push(node(1, 0, 0));
dis[1][0]=0;
while(!Q.empty()){
node cur=Q.top(); Q.pop();
int u=cur.u, pre=cur.pre;
if(dis[u][pre]<cur.d) continue;
for(pii to: g[u]){
int v=to.fi, nxt=pre? 0: to.se;
if(dis[v][nxt]>dis[u][pre]+2*pre*to.se+to.se*to.se)
Q.push(node(v, nxt, dis[v][nxt]=dis[u][pre]+2*pre*to.se+to.se*to.se));
}
}
}
signed main(){
input();
dijkstra();
rep(i, 1, n) printf("%lld ", dis[i][0]==inf? -1: dis[i][0]);
return 0;
}