1. 程式人生 > >[bzoj4144]Petrol——最小生成樹+最短路 dalaos' blogs Some Links

[bzoj4144]Petrol——最小生成樹+最短路 dalaos' blogs Some Links

題目大意:

給定一個n個點、m條邊的帶權無向圖,其中有s個點是加油站。
每輛車都有一個油量上限b,即每次行走距離不能超過b,但在加油站可以補滿。
q次詢問,每次給出x,y,b,表示出發點是x,終點是y,油量上限為b,且保證x點和y點都是加油站,請回答能否從x走到y。

思路:

不難發現如果要順利地完成旅程,一定是從一個加油站跑到另外一個加油站去,並且任意兩個加油站之間地距離不可以超過b。
於是便轉化成了這樣一個問題:加油站為關鍵點,求一條路徑使得兩兩關鍵點之間地路徑長度的最大值最小
這個時候最小生成樹的性質便起到了作用,將兩兩加油站之間的最短距離計算出來,並且只對關鍵點求解最小生成樹,生成樹上的路徑一定是最優方案,具體地證明可以參照Kruskal演算法的執行過程。
顯然關鍵點的個數太多了,如果我們兩兩關鍵點之間求解最短路會T。。
此時就要考慮最小生成樹的性質,最大化地減少連線無用的邊。
考慮關鍵點s到關鍵點t的路徑,若它們中途經過了另外一個關鍵點u,那麼 s

> u , u > t s->u,u->t
一定會更優。
如果s到t的路徑可以被 s > a 1 , a 1
> a 2... a n > t , s->a1,a1->a2...an->t,
這樣的路徑所取代,並且其中任意一條路徑的長度均小於 s > t s->t ,那麼 s > t s->t 還是不要選擇的好。
於是我們可以對於每一個點求出離它最近的關鍵點,記為f[u],如果一條邊(u,v)的f[u],f[v]不同,那麼則將(f[u],f[v])加入候選邊。
如果一條關鍵點之間的路徑不可以被上述方法得到,那麼它肯定不能加入最小生成樹中,即不滿足上面的幾個條件。
即這條不符合條件的路徑,要麼路徑中有其它的關鍵點,要麼可以被一條其它的路徑取代。
具體地實現可以將所有關鍵點加入堆中跑Djkstra,然後對於所有詢問離線即可。

#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
#define debug(x) cout<<#x<<"="<<x<<endl
#define pii pair<ll,int>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;

using namespace std;

void File(){
	freopen("bzoj4144.in","r",stdin);
	freopen("bzoj4144.out","w",stdout);
}

template<typename T>void read(T &_){
	T __=0,mul=1; char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')mul=-1;
		ch=getchar();
	}
	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
	_=__*mul;
}

const int maxn=2e5+10;
int n,s,m,tot;
int beg[maxn],to[maxn<<1],las[maxn<<1],cnte=1,fr[maxn];
ll dis[maxn],w[maxn<<1];
bool ans[maxn];
priority_queue< pii,vector<pii>,greater<pii> >qu;

void add(int u,int v,ll val){
	las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; w[cnte]=val;
	las[++cnte]=beg[v]; beg[v]=cnte; to[cnte]=u; w[cnte]=val;
}

struct Edge{
	int u,v;
	ll len;
	int id;
	bool operator < (const Edge & tt) const {
		return len<tt.len;
	}
}E[maxn],Q[maxn];

void Dijkstra(){
	while(!qu.empty()){
		int u=qu.top().se; ll d=qu.top().fi;
		qu.pop();
		if(d!=dis[u])continue;
		for(int i=beg[u];i;i=las[i]){
			int v=to[i];
			if(d+w[i]<dis[v]){
				fr[v]=fr[u];
				dis[v]=d+w[i];
				qu.push(mk(dis[v],v));
			}
		}
	}
}

int q,fa[maxn];
int find(int x){return fa[x]==x ? x : fa[x]=find(fa[x]);}

void Kruskal(){
	for(int i=2;i<=cnte;i+=2)
		if(fr[to[i]]!=fr[to[i^1]])
			E[++tot]=(Edge){fr[to[i]],fr[to[i^1]],dis[to[i]]+dis[to[i^1]]+w[i],0};
	sort(E+1,E+tot+1);
	REP(i,1,n)fa[i]=i;
	read(q);
	REP(i,1,q)read(Q[i].u),read(Q[i].v),read(Q[i].len),Q[i].id=i;
	sort(Q+1,Q+q+1);
	int p=0;
	REP(i,1,q){
		while(p<tot && E[p+1].len<=Q[i].len){
			++p;
			fa[find(E[p].u)]=find(E[p].v);
		}
		ans[Q[i].id]=(find(Q[i].u)==find(Q[i].v));
	}
	REP(i,1,q)if(ans[i])puts("TAK");
	else puts("NIE");
}

void init(){
	read(n); read(s); read(m);
	memset(dis,63,sizeof(dis));
	int u,v; ll val;
	REP(i,1,s)read(u),qu.push(mk(0,u)),dis[u]=0,fr[u]=u;
	REP(i,1,m)read(u),read(v),read(val),add(u,v,val);
	Dijkstra();
}

int main(){
	File();
	init();
	Dijkstra();
	Kruskal();
	return 0;
}