USACO 2009 January Gold 安全路径

6 篇文章 0 订阅
4 篇文章 0 订阅

Description

Gremlins最近在农场上泛滥,它们经常会阻止牛们从农庄(牛棚_1)走到别的牛棚(牛_i的目的地是牛棚_i)。每一个gremlin只认识牛_i并且知道牛_i一般走到牛棚_i的最短路经。所以它们在牛_i到牛棚_i之前的最后一条牛路上等牛_i,当然,牛不愿意遇到Gremlins,所以准备找一条稍微不同的路经从牛棚_1走到牛棚_i,所以,请你为每一头牛_i找出避免gremlin_i的最短路经的长度。
  和以往一样,农场上的M (2 <= M <= 200,000)条双向牛路编号为1..M并且能让所有牛到达它们的目的地,N(3 <= N <= 100,000)个编号为1..N的牛棚。牛路i连接牛棚a_i (1 <= a_i <= N)和b_i (1 <= b_i <= N)并且需要时间t_i (1 <=t_i <= 1,000)通过。没有两条牛路连接同样的牛棚,所有牛路满足a_i!=b_i。在所有数据中,牛_i使用的牛棚_1到牛棚_i的最短路经是唯一的。

Input

  第一行:两个空格分开的数N和M;
  第2..M+1行:三个空格分开的数a_i, b_i,和t_i

Output

  第1..N-1行:第i行包含一个数,从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间。如果这样的路经不存在,输出-1。

Sample Input

4 5

1 2 2

1 3 2

3 4 4

3 2 1

2 4 3

Sample Output

3

3

6

Hint

【数据范围】
20%的数据满足,N<=200;
50%的数据满足,N<=3000
100%的数据满足,N<=100,000

做这道题之前还不知道有最短路径树这个东西。。。(太菜了233)

既然最短路径是唯一的,显然符合树的性质(可以用反证法证最短路性质)。

题目大意:生成最短路径树后,每次fa[x]->x这条边,用剩余所有边来求1->x的最短路径。

发现是个树,首先就想到树剖。。。

对于一个节点x,有ans=min{dist[u]+dist[v]+w(u,v)-dist[x]},x在lca(u,v)之下,在u,v之上(画图),事实上,也只有满足条件的这种x会被更新到:

1、x在v下面,根据最短路径树的性质,dist[u]+w(u,v)+(v->x)>dist[x]所以不会被更新

2、x在u下面,同上

3、x在lca(u,v)上面,显然= =

发现这个性质之后,就是树剖乱搞了。需要注意的是,u,v会被更新,但lca(u,v)不能被更新。

(码了一个晚自习。。。心力憔悴。。。感动。。。)

#include<bits/stdc++.h>
using namespace std;
#define Inc(i,L,r) for(register int i=(L);i<=(r);++i)
#define Red(i,r,L) for(register int i=(r);i>=(L);--i)
const int N = 1e5+10,E = 2e5+10;
struct Edge{
	int cnt,h[N],w[E<<1],fr[E<<1],to[E<<1],next[E<<1];
	inline void add(int x,int y,int z){
		next[++cnt]=h[x],h[x]=cnt;
		fr[cnt]=x,to[cnt]=y;
		w[cnt]=z;
	}
}e,ex;
bool intree[E<<1];
int n,m;
struct Djkstra{
	struct Node{
		int idx,dist;
		bool operator <(const Node&A) const {
			return dist>A.dist;
		}
	};int fa[N],id[N],dist[N];
	inline void djkstra(){
		memset(dist,63,sizeof(dist));
		priority_queue<Node>Q;
		Q.push((Node){1,dist[1]=0});
		while(!Q.empty()){
			Node x=Q.top();Q.pop();
			for(int p=e.h[x.idx];p;p=e.next[p]){
				int to=e.to[p];if(dist[to]>dist[x.idx]+e.w[p]){
					id[to]=p,fa[to]=x.idx;
					Q.push((Node){to,dist[to]=dist[x.idx]+e.w[p]});
				}
			}
		}
	}
	inline void init(){
		scanf("%d%d",&n,&m);
		e.cnt=1;
		Inc(i,1,m){
			int x,y,z;scanf("%d%d%d",&x,&y,&z);
			e.add(x,y,z),e.add(y,x,z);
		}
	}
}djk;
struct Graph{
	int p[N][18];
	inline void init(){
		Inc(i,1,n)siz[i]=1;
		memset(p,-1,sizeof(p));
		Inc(i,2,n)ex.add(djk.fa[i],i,0);
		Inc(i,2,n)intree[djk.id[i]]=intree[djk.id[i]^1]=1;//树边
	}
	int siz[N],son[N],dep[N];
	int cnt,dfn[N],top[N];
	inline void dfs1(int x,int Fa,int depth){
		for(int P=ex.h[x];P;P=ex.next[P]){
			dfs1(ex.to[P],p[ex.to[P]][0]=x,dep[ex.to[P]]=depth+1);
			siz[x]+=siz[ex.to[P]];
			if(siz[son[x]]<siz[ex.to[P]])son[x]=ex.to[P];
		}
	}
	inline void dfs2(int x,int sp){
		dfn[x]=++cnt;top[x]=sp;
		if(son[x])dfs2(son[x],sp);
		for(int P=ex.h[x];P;P=ex.next[P])if((ex.to[P]^p[x][0])&&(ex.to[P]^son[x]))dfs2(ex.to[P],ex.to[P]);
	}
	inline void ST(){
		int maxdep=log2(n);
		Inc(j,1,maxdep)
			Inc(i,1,n)if(~p[i][j-1])p[i][j]=p[p[i][j-1]][j-1];
	}
	inline int lca(int x,int y){
		if(dep[x]<dep[y])swap(x,y);
		Red(i,log2(dep[x]),0)if(dep[x]-(1<<i)>=dep[y])x=p[x][i];
		if(x==y)return y;
		Red(i,log2(dep[x]),0)if(p[x][i]^p[y][i])x=p[x][i],y=p[y][i];
		return p[x][0];
	}
}g;
struct SegMent{
	struct tree{
		int L,r,mn;
	}t[N<<2];
	#define Ls v<<1
	#define rs v<<1|1
	inline void pushdown(int v){
		t[Ls].mn=min(t[Ls].mn,t[v].mn);
		t[rs].mn=min(t[rs].mn,t[v].mn);
	}
	inline int query(int v,int x){
		if(t[v].L>x||t[v].r<x)return 1<<30;
		if(t[v].L==t[v].r)return t[v].mn;
		pushdown(v);
		return min(query(Ls,x),query(rs,x));
	}
	inline void update(int v,int A,int b,int k){
		if(t[v].L>b||t[v].r<A)return ;
		if(A<=t[v].L&&t[v].r<=b)return t[v].mn=min(t[v].mn,k),void();
		pushdown(v);
		update(Ls,A,b,k),update(rs,A,b,k);
	}
	inline void build(int v,int L,int r){
		t[v]=(tree){L,r,1<<30};
		if(L==r)return ;
		int Mid=L+r>>1;
		build(Ls,L,Mid),build(rs,Mid+1,r);
	}
}t;
inline void modify(int x,int lca,int val){
	int tx=g.top[x];
	while(tx^g.top[lca]){
		t.update(1,g.dfn[tx],g.dfn[x],val);
		x=g.p[tx][0],tx=g.top[x];
	}
	t.update(1,g.dfn[lca]+1,g.dfn[x],val);
}
inline void solv(){
	t.build(1,1,n);
	Inc(i,2,e.cnt)if(!intree[i]){
		int x=e.fr[i],y=e.to[i],lca=g.lca(x,y);
		modify(x,lca,djk.dist[x]+djk.dist[y]+e.w[i]);
		modify(y,lca,djk.dist[x]+djk.dist[y]+e.w[i]);
	}
	Inc(i,2,n){
		int dist=t.query(1,g.dfn[i]);
		cout<<(dist==1<<30?-1:dist-djk.dist[i])<<"\n";
	}
}
int main(){
	djk.init();
	djk.djkstra();
	g.init();
	g.dfs1(1,0,0);
	g.dfs2(1,1);
	g.ST();
	solv();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值