【BZOJ4144】[AMPPZ2014]Petrol 最短路+離線+最小生成樹
阿新 • • 發佈:2017-11-26
判斷 eof etc while cpp ont 原來 有一個 style
輸出q行。第i行輸出第i個詢問的答案,如果可行,則輸出TAK,否則輸出NIE。
1 5 2 6
1 3 1
2 3 2
3 4 3
4 5 5
6 4 5
4
1 2 4
2 6 9
1 5 9
6 5 8
TAK
TAK
NIE
【BZOJ4144】[AMPPZ2014]Petrol
Description
給定一個n個點、m條邊的帶權無向圖,其中有s個點是加油站。 每輛車都有一個油量上限b,即每次行走距離不能超過b,但在加油站可以補滿。 q次詢問,每次給出x,y,b,表示出發點是x,終點是y,油量上限為b,且保證x點和y點都是加油站,請回答能否從x走到y。Input
第一行包含三個正整數n,s,m(2<=s<=n<=200000,1<=m<=200000),表示點數、加油站數和邊數。 第二行包含s個互不相同的正整數c[1],c[2],...c[s](1<=c[i]<=n),表示每個加油站。 接下來m行,每行三個正整數u[i],v[i],d[i](1<=u[i],v[i]<=n,u[i]!=v[i],1<=d[i]<=10000),表示u[i]和v[i]之間有一條長度為d[i]的雙向邊。 接下來一行包含一個正整數q(1<=q<=200000),表示詢問數。 接下來q行,每行包含三個正整數x[i],y[i],b[i](1<=x[i],y[i]<=n,x[i]!=y[i],1<=b[i]<=2*10^9),表示一個詢問。Output
Sample Input
6 4 51 5 2 6
1 3 1
2 3 2
3 4 3
4 5 5
6 4 5
4
1 2 4
2 6 9
1 5 9
6 5 8
Sample Output
TAKTAK
TAK
NIE
題解:比較暴力的想法就是求出所有加油站之間的最短路,但顯然復雜度太高,那麽我們換一種思路,考慮每條邊的貢獻。
先跑多源最短路,求出對於每個點,離它最近的加油站是哪個,記為pre,以及最短路長度dis。然後枚舉每條邊<a,b>,如果pre[a]=pre[b],那麽這條邊顯然沒啥用。否則,我們在新圖中連一條邊<pre[a],pre[b]>,長度為dis[a]+dis[b]+val<a,b>。為什麽可以這樣做呢?因為假如我們從加油站c經過這條邊想走到d,且c!=pre[a]&&c!=pre[b],那麽dist(a,c)>dis[a],dist(b,c)>dis[b],我們可以先不走這條邊,先去a和b加油,再回到這條邊上,剩的油一定是不會比原來少的。
所以我們可以對於新圖求最小生成樹,並離線處理所有詢問,將邊從小到大扔到圖中,用並查集判斷兩個點能否連通即可。
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <queue> #include <utility> #define mp(A,B) make_pair(A,B) using namespace std; const int maxn=200010; int n,K,m,Q,cnt,tot,sum; int pos[maxn],to[maxn<<1],next[maxn<<1],val[maxn<<1],head[maxn],dis[maxn],pre[maxn],inq[maxn],vis[maxn]; int f[maxn],ans[maxn]; priority_queue<pair<int,int> > pq; struct node { int a,b,v,org; }p[maxn],q[maxn]; inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘) f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+(gc^‘0‘),gc=getchar(); return ret*f; } bool cmp(const node &a,const node &b) { return a.v<b.v; } int find(int x) { return (f[x]==x)?x:(f[x]=find(f[x])); } inline void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } int main() { n=rd(),K=rd(),m=rd(); int i,j,u,a,b,c; memset(dis,0x3f,sizeof(dis)),memset(head,-1,sizeof(head)); for(i=1;i<=K;i++) a=pos[i]=rd(),dis[a]=0,pre[a]=i,pq.push(mp(0,a)); for(i=1;i<=m;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c); while(!pq.empty()) { u=pq.top().second,pq.pop(); if(vis[u]) continue; vis[u]=1; for(i=head[u];i!=-1;i=next[i]) if(dis[to[i]]>dis[u]+val[i]) dis[to[i]]=dis[u]+val[i],pre[to[i]]=pre[u],pq.push(mp(-dis[to[i]],to[i])); } for(i=1;i<=n;i++) for(j=head[i];j!=-1;j=next[j]) if(pre[i]<pre[to[j]]) p[++tot].a=pre[i],p[tot].b=pre[to[j]],p[tot].v=dis[i]+dis[to[j]]+val[j]; sort(p+1,p+tot+1,cmp); for(i=1;i<=n;i++) f[i]=i; Q=rd(); for(i=1;i<=Q;i++) q[i].a=pre[rd()],q[i].b=pre[rd()],q[i].v=rd(),q[i].org=i; sort(q+1,q+Q+1,cmp); for(i=j=1;i<=Q;i++) { for(;j<=tot&&p[j].v<=q[i].v;j++) { a=find(p[j].a),b=find(p[j].b); if(a!=b) f[a]=b; } if(find(q[i].a)==find(q[i].b)) ans[q[i].org]=1; } for(i=1;i<=Q;i++) { if(ans[i]) printf("TAK\n"); else printf("NIE\n"); } return 0; }//6 4 5 1 5 2 6 1 3 1 2 3 2 3 4 3 4 5 5 6 4 5 4 1 2 4 2 6 9 1 5 9 6 5 8
【BZOJ4144】[AMPPZ2014]Petrol 最短路+離線+最小生成樹