CF1472G Solution
题解
⭐:有环图上的dp很可能需要多次转移,只得使用类SPFA而非Dijkstra算法
\(d\)数组Dijkstra预处理即可。然后建反向图,dp。
状态:\(dp[i][j]\)表示第i个节点是否(\(j=1/0\))已使用2操作时的答案。
转移方程:
\[dp[v][0]=min(dp[v][0],dp[u][0]),dp[v][1]=min(dp[v][1],dp[u][1])\quad (d_v<d_u)\\
dp[v][1]=min(dp[v][1],dp[u][0])\quad (d_v\ge d_u)
\]
转移时在反图上以节点1为起点进行类SPFA的操作,因为转移后结果不一定全局最优,单个节点需要入队多次(好耶没有被卡)。
目标状态:\(min(dp[i][0],dp[i][1])\)
AC代码
#include<bits/stdc++.h>
#define mp make_pair
using namespace std;
const int N=2e5+10;
int fst[N],nxt[N],v[N],cnt;//原图
int fst2[N],nxt2[N],v2[N],cnt2;//反图
int dis[N],dp[N][2],n; //dis:题目中的d数组
bool vis[N];
priority_queue<pair<int,int> > q;
queue<int> q2;
void add(int x,int y)
{
v[++cnt]=y;
nxt[cnt]=fst[x],fst[x]=cnt;
}
void add2(int x,int y)
{
v2[++cnt2]=y;
nxt2[cnt2]=fst2[x],fst2[x]=cnt2;
}
void dij()
{
for(int i=1;i<=n;i++) vis[i]=0,dis[i]=0x3f3f3f3f;
dis[1]=0;
q.push(mp(0,1));
while(!q.empty())
{
int x=q.top().second; q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=fst[x];i;i=nxt[i])
{
int y=v[i];
if(dis[y]>dis[x]+1)
{dis[y]=dis[x]+1; q.push(mp(-dis[y],y));}
}
}
}
void bfs()
{
memset(vis,0,sizeof(vis));
memset(dp,0x3f,sizeof(dp));
for(int i=1;i<=n;i++) dp[i][0]=dis[i],vis[i]=1;
for(int i=1;i<=n;i++) q2.push(i);
while(!q2.empty())
{
int x=q2.front(); q2.pop();
vis[x]=0;
for(int i=fst2[x];i;i=nxt2[i])
{
int y=v2[i];
if(dis[y]<dis[x])
{
if(dp[x][0]<dp[y][0])
{
dp[y][0]=dp[x][0];
if(!vis[y]) {vis[y]=1; q2.push(y);}
}
if(dp[x][1]<dp[y][1])
{
dp[y][1]=dp[x][1];
if(!vis[y]) {vis[y]=1; q2.push(y);}
}
continue;
}
if(dp[x][0]<dp[y][1])
{
dp[y][1]=dp[x][0];
if(!vis[y]) {vis[y]=1; q2.push(y);}
}
}
}
}
int main()
{
int t,m,x,y;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++) fst[i]=fst2[i]=0;
cnt=cnt2=0;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y),add2(y,x);
}
dij(); bfs();
for(int i=1;i<=n;i++) printf("%d ",min(dp[i][0],dp[i][1]));
printf("\n");
}
return 0;
}