最短路+最小生成树+倍增
图论问题中综合性较强的一题= =(Orz vfk)
比较容易发现,关键的还是有加油站的这些点,其他点都是打酱油的。
也就是说我们重点是要求出 关键点之间的最短路。
这玩意……如果枚举加油站所在的点,然后跑单源最短路什么的……肯定TLE啊。
我们记from[i]表示离 i 最近的关键点,仔细考虑一下,A->B的最短路径上,一定是前一半的from[i]为A,然后走过某条路以后,后一半的from[i]为B。为什么呢?我们不妨设中间出现了一个点x的from[x]=C,那么我们大可以从A走到C,加满油,再从C走到B,这样一定不会差!所以AB之间是否有边就看是否满足这样的条件了……
做法是:先将所有关键点的dist置为0,丢到堆里面做dijkstra,求出每个点的dist和from,然后枚举每条边,如果它连接的两个点满足from[x]!=from[y],那么from[x]和from[y]这两个关键点之间的最短路就找到了。。。
现在我们对只包含关键点的这张图做最小生成树,查询的时候倍增就可以了(又变成了货车运输。。。)
小Trick:图可能不连通,考虑关键点的时候需要分连通块……我一开始光想着如果不在一个连通块内就为NIE,然而忘了既然是多个连通块,那就不能只dfs一次啊!!!
1 /************************************************************** 2 Problem: 4144 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:5076 ms 7 Memory:51424 kb 8 ****************************************************************/ 9 10 //BZOJ 4144 11 #include<vector> 12 #include<queue> 13 #include<cstdio> 14 #include<cstring> 15 #include<cstdlib> 16 #include<iostream> 17 #include<algorithm> 18 #define rep(i,n) for(int i=0;i<n;++i) 19 #define F(i,j,n) for(int i=j;i<=n;++i) 20 #define D(i,j,n) for(int i=j;i>=n;--i) 21 using namespace std; 22 typedef long long LL; 23 inline int getint(){ 24 int r=1,v=0; char ch=getchar(); 25 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 26 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 27 return r*v; 28 } 29 const int N=2e5+10,INF=~0u>>1; 30 /*******************template********************/ 31 32 int head[N],to[N<<1],nxt[N<<1],l[N<<1],cnt; 33 void ins(int x,int y,int z){ 34 to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; l[cnt]=z; 35 } 36 struct edge{ 37 int x,y,w; 38 bool operator < (const edge &b)const {return w<b.w;} 39 }E[N<<1]; 40 int n,m,s,dis[N],from[N],c[N]; 41 42 43 int f[N],sz[N]; 44 inline int getf(int x){return f[x]==x?x:getf(f[x]);} 45 typedef pair<int,int> pii; 46 #define fi first 47 #define se second 48 #define mp make_pair 49 priority_queue<pii,vector<pii>,greater<pii> >Q; 50 bool vis[N]; 51 void dij(){ 52 F(i,1,n) dis[i]=INF; 53 F(i,1,s){ 54 dis[c[i]]=0; 55 from[c[i]]=c[i]; 56 Q.push(mp(0,c[i])); 57 } 58 while(!Q.empty()){ 59 int x=Q.top().se; Q.pop(); 60 if (vis[x]) continue; 61 vis[x]=1; 62 for(int i=head[x];i;i=nxt[i]) 63 if (dis[to[i]]>dis[x]+l[i]){ 64 dis[to[i]]=dis[x]+l[i]; 65 from[to[i]]=from[x]; 66 Q.push(mp(dis[to[i]],to[i])); 67 } 68 } 69 int tot=0; 70 F(i,1,m){ 71 int x=to[i*2-1],y=to[i<<1]; 72 if (from[x]!=from[y]) 73 E[++tot]=(edge){from[x],from[y],dis[x]+dis[y]+l[i<<1]}; 74 } 75 sort(E+1,E+tot+1); 76 memset(head,0,sizeof head); cnt=0; 77 F(i,1,n) f[i]=i,sz[i]=1; 78 F(i,1,tot){ 79 int f1=getf(E[i].x),f2=getf(E[i].y); 80 if (f1!=f2){ 81 if (sz[f1]>sz[f2]) swap(f1,f2); 82 f[f1]=f2; 83 sz[f2]+=sz[f1]; 84 ins(E[i].x,E[i].y,E[i].w); 85 ins(E[i].y,E[i].x,E[i].w); 86 } 87 } 88 } 89 90 int fa[N][20],dep[N],mx[N][20],num,belong[N]; 91 void dfs(int x,int num){ 92 belong[x]=num; 93 F(i,1,19) 94 if (dep[x]>=(1<<i)){ 95 fa[x][i]=fa[fa[x][i-1]][i-1]; 96 mx[x][i]=max(mx[x][i-1],mx[fa[x][i-1]][i-1]); 97 }else break; 98 for(int i=head[x];i;i=nxt[i]) 99 if (to[i]!=fa[x][0]){ 100 fa[to[i]][0]=x; 101 dep[to[i]]=dep[x]+1; 102 mx[to[i]][0]=l[i]; 103 dfs(to[i],num); 104 } 105 } 106 int query(int x,int y){ 107 if (dep[x]<dep[y]) swap(x,y); 108 int t=dep[x]-dep[y],ans=0; 109 F(i,0,19) if (t&(1<<i)) ans=max(ans,mx[x][i]),x=fa[x][i]; 110 D(i,19,0) 111 if (fa[x][i]!=fa[y][i]) 112 ans=max(ans,max(mx[x][i],mx[y][i])), 113 x=fa[x][i],y=fa[y][i]; 114 if (fa[x][0]!=fa[y][0]) return 0; 115 if (x!=y) ans=max(ans,max(mx[x][0],mx[y][0])); 116 return ans; 117 } 118 int main(){ 119 #ifndef ONLINE_JUDGE 120 freopen("4144.in","r",stdin); 121 freopen("4144.out","w",stdout); 122 #endif 123 n=getint(); s=getint(); m=getint(); 124 F(i,1,s) c[i]=getint(); 125 F(i,1,m){ 126 int x=getint(),y=getint(),z=getint(); 127 ins(x,y,z); ins(y,x,z); 128 } 129 dij(); 130 memset(vis,0,sizeof vis); 131 F(i,1,s) if (!belong[c[i]]) dfs(c[i],++num); 132 int q=getint(); 133 while(q--){ 134 int x=getint(),y=getint(),z=getint(); 135 if (belong[x]!=belong[y]) puts("NIE"); 136 else puts((query(x,y)<=z) ? "TAK" : "NIE"); 137 } 138 return 0; 139 }
4144: [AMPPZ2014]Petrol
Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 83 Solved: 36
[Submit][Status][Discuss]
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
输出q行。第i行输出第i个询问的答案,如果可行,则输出TAK,否则输出NIE。
Sample Input
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
Sample Output
TAK
TAK
TAK
NIE