「
「
「数据结构
」
」
」第
5
5
5章 倍增问题
(
(
(后
3
3
3题
)
)
)
目录:
D.货车运输
E.运输计划
F.次小生成树
A . A. A. 例题 1 1 1 货车运输
分析:
可以发现 一些权值较小的边是不会被走过的 那就可以把这些边去掉 也就是建最大生成树
要得到两点之间最小边权的最大值 最大值就是最大生成树解决
最小边权 就在最大生成树上
L
C
A
LCA
LCA 得出的
m
i
n
(
d
i
s
x
,
d
i
s
y
)
min(dis_x,dis_y)
min(disx,disy)就是了.
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int Inf=0x7fffffff;
const int N=1e4+5;
int n,m,head[N],tot,dep[N],f[N],fa[N][21],dis[N][21];
bool vis[N];
struct node{
int to,next,k;
}a[N<<3];
struct node2{
int x,y,dis;
}edge[N<<3];
void add(int x,int y,int k)
{
a[++tot]=(node){y,head[x],k};
head[x]=tot;
}
bool cmp(node2 x,node2 y){return x.dis>y.dis;} //大到小排序
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void kruskal()
{
//大到小排序 就建出最大生成树
for(int i=1;i<=m;i++)
if(find(edge[i].x)!=find(edge[i].y))
{
f[find(edge[i].x)]=find(edge[i].y);
add(edge[i].x,edge[i].y,edge[i].dis);
add(edge[i].y,edge[i].x,edge[i].dis);
}
return;
}
void dfs(int x)
{
vis[x]=1;
for(int i=head[x];i;i=a[i].next)
{
int qwq=a[i].to;
if(vis[qwq]) continue;
dep[qwq]=dep[x]+1;
fa[qwq][0]=x;
dis[qwq][0]=a[i].k;
dfs(qwq);
}
}
int lca(int x,int y)
{
if(find(x)!=find(y)) return -1;
int ans=Inf;
if(dep[y]>dep[x]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])
{
ans=min(ans,dis[x][i]);
x=fa[x][i];
}
if(x==y) return ans;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
ans=min(ans,min(dis[x][i],dis[y][i]));
x=fa[x][i];
y=fa[y][i];
}
ans=min(ans,min(dis[x][0],dis[y][0]));
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
edge[i].x=x;
edge[i].y=y;
edge[i].dis=k;
}
sort(edge+1,edge+m+1,cmp);
for(int i=1;i<=n;i++) f[i]=i;
kruskal();
for(int i=1;i<=n;i++)
if(!vis[i])
{
dep[i]=1;
dfs(i);
fa[i][0]=i;
dis[i][0]=Inf;
}
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
{
fa[j][i]=fa[fa[j][i-1]][i-1];
dis[j][i]=min(dis[j][i-1],dis[fa[j][i-1]][i-1]);
}
int T;
scanf("%d",&T);
while(T--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",lca(x,y));
}
return 0;
}
E . E. E. 例题 5 5 5 运输计划
分析:
求树上距离 就会想到
L
C
A
LCA
LCA
肯定要找长度最长的边用虫洞 但是这条边必须被所有路径经过
那就可以统计边的出现次数 出现次数
=
=
=路径数量 就选出长度最大一条 看最长路径
−
-
−最长边
<
=
<=
<=要求时间
要求时间就是二分的
m
i
d
mid
mid了
r
r
r是最长路径
l
l
l是最长路径
−
-
−最长边
统计边次数 相当于区间
+
1
+1
+1 那就可以差分解决
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
//#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=3e5+5;
int n,m,head[N],tot,fa[N][21],dis[N][21],dep[N];
int ans,val[N],cnt,l,r,mid;
struct node{
int to,next,x;
}edge[N<<1];
struct node2{
int x,y,lca,dis;
}a[N];
bool cmp(node2 x,node2 y){return x.dis>y.dis;}
void add(int x,int y,int k)
{
edge[++tot]=(node){y,head[x],k};
head[x]=tot;
}
void dfs(int x,int father)
{
fa[x][0]=father;
dep[x]=dep[father]+1;
for(int i=head[x];i;i=edge[i].next)
{
int qwq=edge[i].to;
if(qwq!=father)
{
dfs(qwq,x);
dis[qwq][0]=edge[i].x;
}
}
}
int LCA(int x,int y,int p)
{
if(dep[y]>dep[x]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[x][i]]>=dep[y])
{
a[p].dis+=dis[x][i];
x=fa[x][i];
}
if(x==y) return x;
for(int i=20;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
a[p].dis+=dis[x][i]+dis[y][i];
x=fa[x][i];
y=fa[y][i];
}
a[p].dis+=dis[x][0]+dis[y][0];
return fa[x][0];
}
void ret(int x,int father)
{
for(int i=head[x];i;i=edge[i].next)
{
int qwq=edge[i].to;
if(qwq!=father)
{
ret(qwq,x);
val[x]+=val[qwq];
}
}
}
bool check(int x)
{
cnt=0;
memset(val,0,sizeof(val));
for(int i=1;i<=m;i++)
{
if(a[i].dis<=x) break;
val[a[i].lca]--;
val[a[i].x]++;
val[a[i].lca]--;
val[a[i].y]++;
cnt++;
}
ret(1,0);
int maxn=0;
for(int i=1;i<=n;i++)
if(val[i]==cnt)
maxn=max(maxn,dis[i][0]); //选权值最大用虫洞
return a[1].dis-maxn<=mid?1:0;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
add(x,y,k);
add(y,x,k);
l=max(l,k);
}
dfs(1,0);
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
{
fa[j][i]=fa[fa[j][i-1]][i-1];
dis[j][i]=dis[j][i-1]+dis[fa[j][i-1]][i-1];
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a[i].x,&a[i].y);
a[i].lca=LCA(a[i].x,a[i].y,i);
r=max(r,a[i].dis);
}
l=r-l;
sort(a+1,a+m+1,cmp);
while(l<=r)
{
mid=(l+r)>>1;
if(check(mid))
{
ans=mid;
r=mid-1;
}
else l=mid+1;
}
printf("%d",ans);
return 0;
}
F . F. F. 例题 6 6 6 次小生成树
分析:
思路:倍增
L
C
A
+
K
r
u
s
k
a
l
LCA+Kruskal
LCA+Kruskal
K
r
u
s
k
a
l
Kruskal
Kruskal有被证明 任意
(
x
,
y
)
(x,y)
(x,y)之间的边权最大值
<
=
(
x
,
y
)
<=(x,y)
<=(x,y)之间未被选入边的边权
(
(
(毕竟
K
r
u
s
k
a
l
Kruskal
Kruskal是贪心求最小生成树
)
)
)
所以 不严格次小生成树 只要遍历每条不选边的
(
x
,
y
,
k
)
(x,y,k)
(x,y,k) 替换
(
x
,
y
)
(x,y)
(x,y)的最大边
但要求的是 严格次小生成树
不严格在哪
?
?
? 小于等于 等于了就不是严格次小
所以还要找个次大边 用
L
C
A
LCA
LCA分别求 最大边和次大边 最后去替换
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<bitset>
//#pragma GCC optimize(2)
#define reg register
#define Inf 2147483647000000
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=4e5+5,M=9e5+5;
struct node{
ll x,y,k,next;
}a[M<<1],G[N<<1];
ll tot,head[N],fa[N][19],maxn[N][19],minn[N][19],dep[N],father[N],ans=Inf;
ll n,m;
bool vis[M<<1];
inline void add(ll x,ll y,ll k)
{
G[++tot]=(node){x,y,k,head[x]};
head[x]=tot;
}
inline void dfs(ll x,ll father)
{
fa[x][0]=father;
for(reg ll i=head[x];i;i=G[i].next)
{
ll qwq=G[i].y;
if(qwq==father) continue;
dep[qwq]=dep[x]+1ll;
maxn[qwq][0]=G[i].k;
minn[qwq][0]=-Inf;
dfs(qwq,x);
}
}
inline bool cmp(node x,node y){return x.k<y.k;}
inline ll find(ll x){return x==father[x]?x:father[x]=find(father[x]);}
inline ll LCA(ll x,ll y)
{
if(dep[x]<dep[y]) swap(x,y);
for(reg ll i=18;i>=0;i--)
{
if(dep[fa[x][i]]>=dep[y])
x=fa[x][i];
}
if(x==y) return x;
for(reg ll i=18;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
inline ll query(ll x,ll y,ll val)
{
ll res=-Inf;
for(reg ll i=18;i>=0;i--)
{
if(dep[fa[x][i]]>=dep[y])
{
if(val!=maxn[x][i]) res=max(res,maxn[x][i]);
else res=max(res,minn[x][i]);
x=fa[x][i];
}
}
return res;
}
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=m;i++)
scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].k);
sort(a+1,a+m+1,cmp);
for(reg ll i=1;i<=n;i++)
father[i]=i;
ll cnt=0ll;
for(reg ll i=1;i<=m;i++)
{
ll fx=find(a[i].x),fy=find(a[i].y);
if(fx!=fy)
{
cnt+=a[i].k;
father[fx]=fy;
add(a[i].x,a[i].y,a[i].k);
add(a[i].y,a[i].x,a[i].k);
vis[i]=1;
}
}
minn[1][0]=-Inf;
dep[1]=1ll;
dfs(1,-1);
for(reg ll i=1;i<=18;i++)
for(reg ll j=1;j<=n;j++)
{
fa[j][i]=fa[fa[j][i-1]][i-1];
maxn[j][i]=max(maxn[j][i-1],maxn[fa[j][i-1]][i-1]); //最大
minn[j][i]=max(minn[j][i-1],minn[fa[j][i-1]][i-1]); //次大
if(maxn[j][i-1]>maxn[fa[j][i-1]][i-1])
minn[j][i]=max(minn[j][i],maxn[fa[j][i-1]][i-1]);
else if(maxn[j][i-1]<maxn[fa[j][i-1]][i-1])
minn[j][i]=max(minn[j][i],maxn[j][i-1]);
}
for(reg ll i=1;i<=m;i++)
{
if(!vis[i])
{
ll lca=LCA(a[i].x,a[i].y);
ll maxx=query(a[i].x,lca,a[i].k);
ll maxy=query(a[i].y,lca,a[i].k);
ans=min(ans,cnt-max(maxx,maxy)+a[i].k); //替换
}
}
printf("%lld\n",ans);
return 0;
}