题意:
一张大小为n的图,m条边,每次你需要连续走两条边,花费为
(
w
i
+
w
j
)
2
(wi+wj)^2
(wi+wj)2,问你从1走到i(1<=i<=n)的最小花费。
题解:
很明显是dijkstra,但是它每次要走两条边,我们不能枚举儿子的儿子,因为这必然会TLE,所以我们需要存两个状态:走完了和走了一半。那么我们怎么比较走了一半的花费谁优谁劣呢,我们可以看到w的范围只有50,似乎在暗示着什么。那么就设
dp[i][j][k]表示到了第i个点,j:走完还是走了一半,k:如果走了一半,前一条边的花费是多少。
那么就可以转移了:
表示当前走完了(我们用0表示走完),并且这不是赘余状态。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+5;
ll dp[N][2][55];
struct edge{
int to,next,v;
}e[N*2];
int cnt,head[N];
void add(int x,int y,int v){
e[cnt].to=y;
e[cnt].next=head[x];
e[cnt].v=v;
head[x]=cnt++;
}
struct node{
int x,ad;
ll v;
bool operator< (const node& a)const {
return v>a.v;
}
};
priority_queue<node>Q;
void dij(){
Q.push({1,0,0});
while(!Q.empty()){
node u=Q.top();Q.pop();
for(int i=head[u.x];~i;i=e[i].next){
int x=u.x,ne=e[i].to,v=e[i].v,ad=u.ad;
if(!ad&&dp[x][0][0]==u.v){
if(dp[ne][1][v]==-1||dp[ne][1][v]>dp[x][0][0])
dp[ne][1][v]=dp[x][0][0],Q.push({ne,v,u.v});
}
if(ad&&dp[x][1][ad]==u.v){
if(dp[ne][0][0]==-1||dp[ne][0][0]>dp[x][1][ad]+(ad+v)*(ad+v))
dp[ne][0][0]=dp[x][1][ad]+(ad+v)*(ad+v),Q.push({ne,0,dp[ne][0][0]});
}
}
}
}
int main()
{
memset(head,-1,sizeof(head));
int n,m,x,y,v;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&x,&y,&v),add(x,y,v),add(y,x,v);
memset(dp,-1,sizeof(dp));
dp[1][0][0]=0;
dij();
for(int i=1;i<=n;i++)
printf("%lld ",dp[i][0][0]);
return 0;
}