LG P2483 【模板】k短路([SDOI2010]魔法猪学院)

版权声明:本文为博主原创文章,未经博主允许必须转载。 https://blog.csdn.net/qq_35950004/article/details/88787346

题目
首先求出以 n n 为根的最短路径树。
非树边 i i 的权值重置为: c s t [ i ] + d i s [ t o [ i ] ] d i s [ f r o m [ i ] ] cst[i]+dis[to[i]]-dis[from[i]]
那么选非树边 i i 就代表从 f r o m [ i ] from[i] t o [ i ] to[i] 走这条边,而非最短路径树上的边。
那么我们需要能够组成一个边序列使得其构成一个合法的路径。
求出第k小的路径。
考虑归并。
如果我们可以把所有的边序列分成几个类,并且求出每个类的最小值方案,那么最小的路径就是每个类的最小值方案中的最小值。
考虑第二小,发现第一小的路径已经失去了竞争资格。
那么删去第一小的路径后,如果第一小路径的那一类会变成一个或多个类使得每个类没有交集,他们的并集只是之前的那一类除去第一小路径,那么我们就可以把那一类分裂成这几类从而达到删除第一小路径的目的。
继续执行求最小值等过程即可推至第k小路径。
对于这个题,一开始的类为以边 i i 为路径上第一条非树边的路径。
显然每个类的最小值方案就是只有 i i 这一条非树边。
然后删去只选 i i 的方案,我们可以分裂成第一条是 i i ,第二条是 j j 那些类。
那么每一类的最小值其实就是已经确定的边的和。
我们只需要快速求多个类的最小值。
用优先队列即可实现,每次拿出队头,再将拓展后的放入。
发现拓展十分费时。
改一下拓展方案。
第一条是 i i 且不只有 i i 分裂成:第一条是 i i 第二条是 i i 后能接的最小的边 j j 的路径且不只有 i i j j 和 第一条是 i i 第二条不是 i i 后能接的最小的边 j j 的路径
第一类的最小值为 i + j + j i+j+j后能接的最小的边
第二类的最小值为 i + i i+i后能接第二小的边
注意我们不考虑 i + j i+j 因为 i + j i+j 显然比第二类的最小值小,相当于我们把求最小值集体往前移了一次。
然后求第二小就行了。
发现每个点维护一个堆也是 O ( n m ) O(nm)
那么就可持久化来共用节点。

#include<bits/stdc++.h>
#define maxn 5005
#define maxm 200005
#define eps 1e-8
using namespace std;

int n,m;
double E;
int info[maxn],Prev[maxm],to[maxm],cnt_e=1;
double cst[maxm];
void Node(int u,int v,double c){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v,cst[cnt_e]=c; }
bool usd[maxn];
int fa[maxn],faed[maxn];
double dis[maxn];

	
struct node{
	int ch[2],to,dep;
	double cst;
	node(int to=0,double cst=0):to(to),cst(cst){dep=1,ch[0]=ch[1]=0;}
}t[maxm * 10];	
int cnt,rt[maxn];

int cpy(int now){
	t[++cnt] = t[now];
	return cnt;
}

void merge(int &now,int l,int r){
	if(!l || !r){ now = l+r; return; }
	if(t[l].cst < t[r].cst) now = cpy(l) , merge(t[now].ch[1],t[l].ch[1],r);
	else now = cpy(r) , merge(t[now].ch[1],l,t[r].ch[1]);
	if(t[t[now].ch[1]].dep > t[t[now].ch[0]].dep) swap(t[now].ch[0],t[now].ch[1]);
	t[now].dep = t[t[now].ch[1]].dep + 1;
}

void dfs(int now){
	for(int i=info[now];i;i=Prev[i])
		if(faed[to[i]] == i){
			//printf("%d %d\n",to[i],now);
			merge(rt[to[i]],rt[to[i]],rt[now]);
			dfs(to[i]);
		}
}


int main(){
	scanf("%d%d%lf",&n,&m,&E);
	for(int i=1,u,v;i<=m;i++){
		double c;
		scanf("%d%d%lf",&u,&v,&c);
		Node(v,u,c);
	}
	priority_queue<pair<double,int>,vector<pair<double,int> >,greater<pair<double,int> > >q;
	memset(dis,0x7f,sizeof dis);
	dis[n] = 0 , q.push(make_pair(dis[n],n));
	for(int now;!q.empty();){
		now = q.top().second , q.pop();
		if(usd[now]) continue;
		usd[now] = 1;
		for(int i=info[now];i;i=Prev[i])
			if(dis[to[i]] > dis[now] + cst[i] + eps){
				dis[to[i]] = dis[now] + cst[i] , fa[to[i]] = now;
				faed[to[i]] = i;
				q.push(make_pair(dis[to[i]],to[i]));
			}
	}
	//for(int i=1;i<=n;i++) printf("$%d %.3lf\n",fa[i],dis[i]);
	for(int i=1;i<=n;i++)
		for(int j=info[i];j;j=Prev[j])
			if(j != faed[to[j]] && to[j]!=n){
				t[++cnt] = node(i,dis[i] + cst[j] - dis[to[j]]);
				//printf("%d %d %.3lf\n",i,to[j],dis[i] + cst[j] - dis[to[j]]);
				merge(rt[to[j]],rt[to[j]],cnt);
			}
	dfs(n);
	int ans = 1; 
	E -= dis[1];
	//printf("%d\n",rt[1]);
	if(rt[1]) q.push(make_pair(dis[1]+t[rt[1]].cst,rt[1]));
	for(int now;!q.empty();){
		double val = q.top().first;
		//printf("%.3lf %.3lf\n",val,E);
		if(val > E + eps) break;
		ans ++ , E -= val;
		//printf("%.3lf\n",val);
		now = q.top().second , q.pop();
		for(int i=0;i<2;i++)
			if(t[now].ch[i])
				q.push(make_pair(val-t[now].cst+t[t[now].ch[i]].cst,t[now].ch[i]));
		if(rt[t[now].to]) 
			q.push(make_pair(val+t[rt[t[now].to]].cst,rt[t[now].to]));
	}
	printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/88787346