【2019 CCPC 哈爾濱站 H. Highway Buses】【點分樹+最短路】

題意

題目鏈接
有一個nn個點mm條邊的無向連通圖,從每個點出發有一個花費cic_i和限制距離fif_i,表示可以通過cic_i的花費到達與它最短距離不超過fif_i的點。有TT個時刻,每過一個時刻點ii的花費要加上wiw_i。對k=1,...,nk=1,...,n,求選擇某一個時刻並從11出發,走到點kk的最小花費。
n200000,mn+50n\le 200000,m\le n+50

分析

由於花費是一次函數,所以要麼選擇第一個時刻要麼選擇最後一個時刻,做兩遍取較優值即可。
將圖看成是生成樹加上不超過51條非樹邊。如果只考慮樹邊,那麼可以把點分樹建出來,對每個分治重心按到它的距離從小到大維護一個隊列。注意到一個點的disdis在第一次被更新時肯定是最優的,用一個堆維護disx+cxdis_x+c_x的最小值,每次取堆頂,然後遍歷它在點分樹上的祖先,不斷彈出對應的隊首元素,這部分的時間複雜度爲O(nlogn)O(n\log n)
若經過一條非樹邊(u,v)(u,v),則必定會經過點uu,令點uu爲關鍵點,則最多隻有不超過5151個關鍵點。對每個關鍵點bfs出它到其他所有點的最短路並按距離從小到大維護一個隊列,每次仍然不斷取出隊首即可,這部分時間複雜度爲O(51n)O(51n)
於是總的複雜度爲O(nlogn+51n)O(n\log n+51n)

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章