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][i1][k]][i1][l]

da[j][i][k]=da[j][i1][k]+da[f[j][i1][k]][i1][l]

db[j][i][k]=db[j][i1][k]+db[f[j][i1][k]][i1][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;
}

posted @   jt0007  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示