「
「
「数据结构
」
」
」第
5
5
5章 倍增问题
(
(
(前
3
3
3题
)
)
)
目录:
A.查找编号
B.开车旅行
C.树上距离
A . A. A. 例题 1 1 1 查找编号
分析:
直接
l
o
w
e
r
lower
lower_
b
o
u
n
d
(
)
bound()
bound()就搞完了
也可以倍增 比它小就一直跳 最后输出倍增到的位置或
−
1
-1
−1
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define reg register
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,m,a[N];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
while(m--)
{
int x,p=0;
scanf("%d",&x);
for(int i=20;i>=0;i--)
if(p+(1<<i)<=n)
if(a[p+(1<<i)]<x)
p+=(1<<i); //倍增跳小于当前数
if(a[p+1]==x&&p<=n)
printf("%d ",p+1);
else printf("-1 ");
}
return 0;
}
B . B. B. 例题 2 2 2 开车旅行
分析:
做着做着 这题就紫掉蓝了。。
模拟
+
+
+倍增 细节也挺多
用链表两边拓展 找到最近的两个 再从链表删除当前点
这样很慢 那倍增就好了 但两个人是交替走的
就可以用
f
0
/
1
,
s
t
,
k
f_{0/1,st,k}
f0/1,st,k 表示第一步是
A
/
B
A/B
A/B走 起点是
s
t
st
st 走
2
k
2^k
2k能到达的地方
求距离也同时用
d
i
s
0
/
1
,
s
t
,
k
dis_{0/1,st,k}
dis0/1,st,k记录
第一问就直接找最小比值 第二问就是倍增 累计
A
/
B
A/B
A/B距离
一开始我第一问一直
n
t
nt
nt 打成
(
A
/
B
)
×
1.0
(A/B)\times1.0
(A/B)×1.0
(
(
(这些都是除号
)
)
) 后面改成
A
/
B
×
1.0
A/B\times1.0
A/B×1.0 但输出不对
然后找了题解的标来改 发现要改成
1.0
×
A
/
B
1.0\times A/B
1.0×A/B 就过样例了 但还是
W
A
WA
WA
结果找最小那里 要先判断
B
B
B走的距离
>
0
>0
>0 然后就过了 因为题目:
CODE:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define reg register
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
const int N=1e5+5;
struct node{
int x,id;
}a[N];
bool cmp(node x,node y){return x.x<y.x;}
int n,m,pre[N],nxt[N],ans,loc[N],f[N][2][21];
ll dis[N][2][21],A,B,T;
db minn=2147483647;
bool Com(int p,int l,int r)
{
if(!l) return 0;
if(!r) return 1;
return a[p].x-a[l].x<=a[r].x-a[p].x?1:0; //找最近
}
int Scom(int p,int l,int r)
{
if(!l) return a[r].id;
if(!r) return a[l].id;
return a[p].x-a[l].x<=a[r].x-a[p].x?a[l].id:a[r].id; //找第二近
}
void go_step1() //走第一步
{
for(int i=1;i<=n;i++)
{
loc[a[i].id]=i;
pre[i]=i-1;
nxt[i]=i+1;
}
pre[1]=nxt[n]=0;
for(int i=1;i<=n;i++)
{
int p=loc[i],l=pre[p],r=nxt[p];
if(Com(p,l,r)){
f[i][1][0]=a[l].id;
f[i][0][0]=Scom(p,pre[l],r);
}else{
f[i][1][0]=a[r].id;
f[i][0][0]=Scom(p,l,nxt[r]);
}
//f[i][0][0]=Com(p,l,r)?Scom(p,pre[l],r):Scom(p,l,nxt[r]);
if(l) nxt[l]=r;
if(r) pre[r]=l;
}
}
void go_step2() //先小A再小B
{
for(int i=1;i<=n;i++)
{
f[i][0][1]=f[f[i][0][0]][1][0];
dis[i][0][1]=abs(a[loc[i]].x-a[loc[f[i][0][0]]].x);
dis[i][1][1]=abs(a[loc[f[i][0][1]]].x-a[loc[f[i][0][0]]].x);
}
}
void drive() //倍增正常走
{
for(int j=2;j<=20;j++)
for(int i=1;i<=n;i++)
{
f[i][0][j]=f[f[i][0][j-1]][0][j-1];
dis[i][0][j]=dis[i][0][j-1]+dis[f[i][0][j-1]][0][j-1];
dis[i][1][j]=dis[i][1][j-1]+dis[f[i][0][j-1]][1][j-1];
}
}
void ABstep(int st,ll x) //看倍增走到哪里
{
A=B=0;
for(int i=20;i>=1;i--)
if(f[st][0][i]&&A+B+dis[st][0][i]+dis[st][1][i]<=x)
{
A+=dis[st][0][i];
B+=dis[st][1][i]; //分别算贡献
st=f[st][0][i];
}
if(f[st][0][0]&&A+B+dis[st][0][1]<=x) A+=dis[st][0][1];
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
go_step1();
go_step2();
drive();
scanf("%lld%d",&T,&m);
for(int i=1;i<=n;i++)
{
ABstep(i,T);
if(B&&1.0*A/B<minn)
{
minn=1.0*A/B;
ans=i;
}
}
printf("%d\n",ans);
while(m--)
{
int st;
scanf("%d%lld",&st,&T);
ABstep(st,T);
printf("%lld %lld\n",A,B);
}
return 0;
}
C . C. C. 例题 3 3 3 树上距离
分析:
L
C
A
LCA
LCA模板
对于
(
x
,
y
)
(x,y)
(x,y)的树上距离
就是
x
x
x到根节点的距离
+
y
+y
+y到根节点的距离
−
2
×
(
x
,
y
)
-2\times (x,y)
−2×(x,y)的
L
C
A
LCA
LCA到根节点的距离
比如这幅图中
(
5
,
6
)
(5,6)
(5,6)的树上距离
3
3
3即
5
5
5到
1
1
1的距离
:
2
+
6
:2+6
:2+6到
1
1
1的距离
:
3
−
2
×
(
5
,
6
)
:3-2\times (5,6)
:3−2×(5,6)的
L
C
A
LCA
LCA
2
2
2到
1
1
1的距离
:
1
:1
:1
=
2
+
3
−
2
×
1
=
3
=2+3-2\times1=3
=2+3−2×1=3
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=1e4+5;
int n,m,head[N],tot,fa[21][N],dis[21][N],dep[N];
struct node{
int to,next,x;
}a[N<<2];
void add(int x,int y,int k)
{
a[++tot]=(node){y,head[x],k};
head[x]=tot;
}
void dfs(int x,int father)
{
dep[x]=dep[father]+1;
for(int i=head[x];i;i=a[i].next)
{
int qwq=a[i].to;
if(qwq!=father)
{
fa[0][qwq]=x;
dis[0][qwq]=a[i].x;
dfs(qwq,x);
}
}
}
int lca(int x,int y)
{
if(dep[y]>dep[x]) swap(x,y);
for(int i=20;i>=0;i--)
if(dep[fa[i][x]]>=dep[y])
x=fa[i][x];
if(x==y) return x;
for(int i=20;i>=0;i--)
if(fa[i][x]!=fa[i][y])
{
x=fa[i][x];
y=fa[i][y];
}
return fa[0][x]; //求LCA
}
int query(int x,int y)
{
int LCA=lca(x,y);
if(LCA==x) return dis[20][y]-dis[20][x];
if(LCA==y) return dis[20][x]-dis[20][y];
return dis[20][x]+dis[20][y]-2*dis[20][LCA]; //求树上距离
}
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);
}
dfs(1,0);
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
{
fa[i][j]=fa[i-1][fa[i-1][j]];
dis[i][j]=dis[i-1][j]+dis[i-1][fa[i-1][j]];
}
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
printf("%d\n",query(x,y));
}
return 0;
}