P1967 [NOIP2013 提高组] 货车运输
首先由于两地之间的路径上的最小权重,为货车的最大载重量,我们希望其比较大。因此,想到了最大生成树,构建一个新的图。由于有可能两地之间不连接,所以判断答案时要用到并查集看看这两个点是否在一个连通块内。由于树内两个点的路径是唯一的,进行LCA。分别对每个连通块进行dfs,获得其深度以及倍增的祖宗节点,和到该祖宗节点路径上的最小边权值。这样,使用LCA时,一边获得其祖宗,一边计算路径上的最小边权,得到结果。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll inf = 1e18;
long long int mod = 1000000007;
const int maxN = 1e4+10;
const int maxM= 5e4+10;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
struct Edge{
int u,v;
int w;
bool operator<(const Edge &rhs)const{
return w>rhs.w;
}
}edge[maxM];
struct Tree{
int u,v,next;
int w;
}tree[2*maxM];
int f[maxN],head[maxN];
int n,m,q,c;
void add(int u,int v,int w){
tree[++c].u=u;
tree[c].v=v;
tree[c].w=w;
tree[c].next=head[u];
head[u]=c;
};
int find(int x){
if(x!=f[x])
f[x]=find(f[x]);
return f[x];
}
bool Kruskal(){
int cnt=0;
for(int i=1;i<=m;i++){
if(cnt==n-1)
break;
int x=find(edge[i].u);
int y=find(edge[i].v);
if(x==y)
continue;
add(edge[i].u,edge[i].v,edge[i].w);
add(edge[i].v,edge[i].u,edge[i].w);
f[x]=y;
cnt++;
}
if(cnt==n-1)
return true;
else
return false;
}
int depth[maxN], fa[maxN][22], lg[maxN],h[maxN][22],vis[maxN];
void dfs(int now, int fath,int w) {
fa[now][0] = fath;
h[now][0]=w;
vis[now]=1;
depth[now] = depth[fath] + 1;
for (int i = 1; i <= lg[depth[now]]; i++) {
fa[now][i] = fa[fa[now][i - 1]][i - 1];
h[now][i]=min(h[fa[now][i-1]][i-1],h[now][i-1]);
}
for (int i = head[now]; i != 0; i = tree[i].next) {
if (tree[i].v != fath)
dfs(tree[i].v, now,tree[i].w);
}
}
int LCA(int x, int y) {
if (depth[x] < depth[y])
swap(x, y);
int ans=INT_MAX;
while (depth[x] > depth[y]) {
int tmp = fa[x][lg[depth[x] - depth[y]]];
ans=min(ans,h[x][lg[depth[x] - depth[y]]]);
x=tmp;
}
if (x == y){
return ans;
}
for (int k = lg[depth[x]]; k >= 0; k--) {
if (fa[x][k] != fa[y][k]) {
ans=min(ans,h[x][k]);
ans=min(ans,h[y][k]);
x = fa[x][k];
y = fa[y][k];
}
}
ans=min(ans,min(h[x][0],h[y][0]));
return ans;
}
int main() {
IOS
cin>>n>>m;
lg[1]=0;
for(int i=2;i<=n;i++){
lg[i]=lg[i/2]+1;
}
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
edge[i].u=x,edge[i].v=y;
edge[i].w=z;
}
sort(edge+1,edge+1+m);
for(int i=1;i<=n;i++){
f[i]=i;
}
Kruskal();
for(int i=1;i<=n;i++){
if(vis[i]==0)
dfs(i,0,0);
}
cin>>q;
for(int i=1;i<=q;i++){
int x,y;
cin>>x>>y;
int fx=find(x);
int fy=find(y);
if(fx!=fy){
cout<<-1<<endl;
continue;
}
cout<<LCA(x,y)<<endl;
}
return 0;
}
P1613 跑路
题目要求到1到n点的最短时间,但是只给了每条路径长度为1。而每次跑2的t次方米只要一秒,所以需要把图转成每个点之间到达所花费的时间作为边权。利用倍增思想p[u][v][0]表示点u到点v存在2的0次方米的路径。再利用Floyd计算每个点到达其他的点的时间,构成图
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxN = 1e3 + 10;
const int maxM = 1e6 + 10;
const int inf = 1e6;
int n,m;
int p[55][55][34];
int main() {
cin>>n>>m;
vector<vector<int>>a(n+1,vector<int>(n+1,inf));
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
a[u][v]=1;
p[u][v][0]=1;
}
for(int t=1;t<=32;t++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
if(p[i][j][t-1]==1&&p[j][k][t-1]==1){
p[i][k][t]=1;
a[i][k]=1;
}
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
a[i][j]=min(a[i][j],a[i][k]+a[k][j]);
}
}
}
cout<<a[1][n]<<endl;
return 0;
}