ybtoj倍增练习
开车旅行
首先很显然我们要预处理A和B在该点下一步要走到的位置,我们可以先按照海拔排序,对于B来说它的下一步要么是它的前驱,要么是它的后继,对于A来说它的下一步要么是它的前驱或者前驱的前驱,要么是它的后继或者后继的后继,还要支持在走过这个点之后将其删除很显然我们可以用链表维护
接下来我们预处理出A和B从x点出发走多少次的位置和距离,为什么考虑倍增呢,因为我们可以发现一个很好的性质,即每次的选择都是固定的。
当i!=1时,每回我们走2i步时开车的人不变,所以我们可以推出转移式子
即
l=k (i ! =1)
l=k (i = 1)
f[j][i][k]=f[f[j][i−1][k]][i−1][l]
da[j][i][k]=da[j][i−1][k]+da[f[j][i−1][k]][i−1][l]
db[j][i][k]=db[j][i−1][k]+db[f[j][i−1][k]][i−1][l]
对于问题1我们直接暴力枚举每一个起点,并把乘法转化为乘法避免精度问题
对于问题2同理
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e5+5;
int goa[maxn];
int gob[maxn];
int t;
int pos[maxn];
int f[maxn][21][2];//到达的城市(第2^i天k将要开车将要到达的城市)
int da[maxn][21][2];//a走过的距离
int db[maxn][21][2];//b走过的距离
int n,m;
int la,lb;
struct node{
int h,id,pre,nxt;
friend bool operator < (node a,node b){
return a.h<b.h;
}
}p[maxn];
int maxx(int x,int y,int now){
if(!y) return x;
if(!x) return y;
if(abs(p[now].h-p[x].h)<=abs(p[y].h-p[now].h)) return x;
else return y;
}
void cal(int st,int x){
la=lb=0;
int k=0;
for(int i=20;i>=0;i--){
if(f[st][i][k]&&da[st][i][k]+db[st][i][k]<=x){
x-=da[st][i][k];
x-=db[st][i][k];
la+=da[st][i][k];
lb+=db[st][i][k];
// if(!i) k^=1;
st=f[st][i][k];
}
}
}
signed main()
{
// freopen("P1081_2.in","r",stdin);
// freopen("fuck.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>p[i].h;
p[i].id=i;
}
sort(p+1,p+1+n);
for(int i=1;i<=n;i++){
pos[p[i].id]=i;
p[i].pre=i-1;
p[i].nxt=i+1;
}
p[1].pre=p[n].nxt=0;
for(int i=1;i<n;i++){
int now=pos[i],lst=p[now].pre,nxt=p[now].nxt;
if(lst&&((abs(p[now].h-p[lst].h)<=abs(p[now].h-p[nxt].h))||(!nxt))){
gob[i]=p[lst].id;
goa[i]=p[maxx(p[lst].pre,nxt,now)].id;
}
else{
gob[i]=p[nxt].id;
goa[i]=p[maxx(lst,p[nxt].nxt,now)].id;
}
if(p[now].nxt) p[p[now].nxt].pre=p[now].pre;
if(p[now].pre) p[p[now].pre].nxt=p[now].nxt;
}
for(int i=1;i<=n;i++){
if(goa[i]){
f[i][0][0]=goa[i];
da[i][0][0]=abs(p[pos[i]].h-p[pos[goa[i]]].h);
db[i][0][0]=0;
}
if(gob[i]){
f[i][0][1]=gob[i];
da[i][0][1]=0;
db[i][0][1]=abs(p[pos[i]].h-p[pos[gob[i]]].h);
}
}
for(int i=1;i<=20;i++){
for(int j=1;j<=n;j++){
for(int k=0;k<=1;k++){
int l=k;
if(i==1) l^=1;
if(f[j][i-1][k]) f[j][i][k]=f[f[j][i-1][k]][i-1][l];
if(f[j][i][k]){
da[j][i][k]=da[j][i-1][k]+da[f[j][i-1][k]][i-1][l];//前一半必然是自己开的,后一半在i=1时是对方开的
db[j][i][k]=db[j][i-1][k]+db[f[j][i-1][k]][i-1][l];
}
}
}
}
int ansa=1,ansb=0;
int xx;
cin>>xx;
int pp=0;
for(int i=1;i<n;i++){
cal(i,xx);
if(!lb) la=1;
if(la*ansb<lb*ansa||(la*ansb==lb*ansa&&p[pos[i]].h>p[pos[pp]].h)){
ansa=la,ansb=lb,pp=i;
}
}
cout<<pp<<'\n';
cin>>m;
int s,x;
for(int i=1;i<=m;i++){
cin>>s>>x;
cal(s,x);
cout<<la<<" "<<lb<<'\n';
}
return 0;
}
跑路
按照floyd的思路转移,考虑两个点之间是否有一条能够用2k跑完的路径
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=105;
int g[maxn][maxn];
int f[maxn][maxn][maxn];
int n,m;
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
memset(g,0x3f,sizeof(g));
for(int i=1;i<=m;i++){
int a,b;
cin>>a>>b;
g[a][b]=1;
f[a][b][0]=1;
}
for(int q=1;q<=32;q++){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(f[i][k][q-1]&&f[k][j][q-1]){
f[i][j][q]=1;
g[i][j]=1;
}
}
}
}
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
}
}
cout<<g[1][n];
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下