題意
題目鏈接
有一個個點條邊的無向連通圖,從每個點出發有一個花費和限制距離,表示可以通過的花費到達與它最短距離不超過的點。有個時刻,每過一個時刻點的花費要加上。對,求選擇某一個時刻並從出發,走到點的最小花費。
分析
由於花費是一次函數,所以要麼選擇第一個時刻要麼選擇最後一個時刻,做兩遍取較優值即可。
將圖看成是生成樹加上不超過51條非樹邊。如果只考慮樹邊,那麼可以把點分樹建出來,對每個分治重心按到它的距離從小到大維護一個隊列。注意到一個點的在第一次被更新時肯定是最優的,用一個堆維護的最小值,每次取堆頂,然後遍歷它在點分樹上的祖先,不斷彈出對應的隊首元素,這部分的時間複雜度爲。
若經過一條非樹邊,則必定會經過點,令點爲關鍵點,則最多隻有不超過個關鍵點。對每個關鍵點bfs出它到其他所有點的最短路並按距離從小到大維護一個隊列,每次仍然不斷取出隊首即可,這部分時間複雜度爲
於是總的複雜度爲
代碼
#include<bits/stdc++.h>
#define mp std::make_pair
#define pb push_back
typedef long long LL;
typedef std::pair<int,int> pi;
typedef std::pair<LL,int> PI;
const int N=200005;
const int M=55;
const LL inf=(LL)1e16;
int n,m,T,cnt,f[N],mer[N],size[N],mx_size[N],root,sum,tim,fa[N],last[N],pos[N],pts[M],pts_dis[M][N],tot,tag[N],now_dis[N];
struct edge{int to,next,op;}e[(N+M)*2];
std::queue<int> que;
std::priority_queue<PI> pri_que;
std::queue<pi> que1[N],pts_que[M];
bool vis[N];
LL w[N],c[N],dis[N],ans[N];
std::vector<int> root_dis[N];
void addedge(int u,int v,int op)
{
e[++cnt].to=v;e[cnt].op=op;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].op=op;e[cnt].next=last[v];last[v]=cnt;
}
int find(int x)
{
if (mer[x]==x) return x;
else return mer[x]=find(mer[x]);
}
void init()
{
scanf("%d%d%d",&n,&m,&T);
for (int i=1;i<=n;i++) scanf("%d%lld%lld",&f[i],&c[i],&w[i]),mer[i]=i;
for (int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if (find(mer[x])!=find(mer[y])) mer[find(x)]=find(y),addedge(x,y,0);
else
{
addedge(x,y,1);
if (!pos[x]) pos[x]=++tot,pts[tot]=x;
}
}
}
void bfs()
{
for (int k=1;k<=tot;k++)
{
int s=pts[k];
memset(pts_dis[k],0,sizeof(pts_dis[k]));
pts_dis[k][s]=1;que.push(s);
while (!pts_que[k].empty()) pts_que[k].pop();
while (!que.empty())
{
int x=que.front();que.pop();
pts_que[k].push(mp(x,pts_dis[k][x]-1));;
for (int i=last[x];i;i=e[i].next)
if (!pts_dis[k][e[i].to])
pts_dis[k][e[i].to]=pts_dis[k][x]+1,que.push(e[i].to);
}
for (int i=1;i<=n;i++) pts_dis[k][i]--;
}
}
void get_root(int x,int fa)
{
size[x]=1;mx_size[x]=0;
for (int i=last[x];i;i=e[i].next)
{
if (e[i].op==1||e[i].to==fa||vis[e[i].to]) continue;
get_root(e[i].to,x);
size[x]+=size[e[i].to];
mx_size[x]=std::max(mx_size[x],size[e[i].to]);
}
mx_size[x]=std::max(mx_size[x],sum-size[x]);
if (!root||mx_size[x]<mx_size[root]) root=x;
}
void work(int s)
{
que.push(s);tim++;tag[s]=tim;now_dis[s]=0;
while (!que.empty())
{
int x=que.front();que.pop();
que1[s].push(mp(x,now_dis[x]));root_dis[x].pb(now_dis[x]);
for (int i=last[x];i;i=e[i].next)
if (!e[i].op&&tag[e[i].to]!=tim&&!vis[e[i].to])
tag[e[i].to]=tim,now_dis[e[i].to]=now_dis[x]+1,que.push(e[i].to);
}
}
void solve(int x)
{
vis[x]=1;
work(x);
get_root(x,0);
for (int i=last[x];i;i=e[i].next)
{
if (vis[e[i].to]||e[i].op) continue;
root=0;sum=size[e[i].to];get_root(e[i].to,x);
fa[root]=x;
solve(root);
}
}
void build()
{
sum=n;root=0;get_root(1,0);
solve(root);
}
void calc()
{
memset(vis,0,sizeof(vis));
dis[1]=0;
for (int i=2;i<=n;i++) dis[i]=inf;
pri_que.push(mp(-c[1],1));
while (!pri_que.empty())
{
int x=pri_que.top().second;pri_que.pop();
if (vis[x]) continue;
vis[x]=1;
int now=0;
for (int t=x;t;t=fa[t],now++)
{
int dep=f[x]-root_dis[x][root_dis[x].size()-now-1];
while (!que1[t].empty()&&que1[t].front().second<=dep)
{
int y=que1[t].front().first;que1[t].pop();
if (dis[y]==inf) dis[y]=dis[x]+c[x],pri_que.push(mp(-dis[y]-c[y],y));
}
}
for (int i=1;i<=tot;i++)
{
int dep=f[x]-pts_dis[i][x];
while (!pts_que[i].empty()&&pts_que[i].front().second<=dep)
{
int y=pts_que[i].front().first;pts_que[i].pop();
if (dis[y]==inf) dis[y]=dis[x]+c[x],pri_que.push(mp(-dis[y]-c[y],y));
}
}
}
for (int i=1;i<=n;i++) ans[i]=std::min(ans[i],dis[i]);
}
int main()
{
init();
bfs();
build();
for (int i=1;i<=n;i++) ans[i]=inf;
calc();
for (int i=1;i<=n;i++)
{
c[i]+=(LL)w[i]*(T-1);
while (!que1[i].empty()) que1[i].pop();
}
bfs();
memset(vis,0,sizeof(vis));
build();
calc();
for (int i=1;i<=n;i++) printf("%lld\n",ans[i]);
return 0;
}