【NOIP2012 提高组 day1】开车旅行

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
倍增优化DP
链表 或者 平衡树 预处理(set)离每一个结点最近的点和第二近的点。
F(I,j,k)表示当前在j点走2^ i天到达的下一个点 k=0/1代表是A或者B先开车。DA(I,J,K)表示当前在j点走2^i天到达的下一个点A一共走的距离 k=0/1代表是A或者B先开车
DB(I,J,K)表示当前在j点走2^i天到达的下一个点B一共走的距离 k=0/1代表是A或者B先开车
就可以啦orz

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100050;
const double inf=1e17;
struct node{
	ll id,l,r;
	ll h;
}p[N];
ll pos[N],f[20][100001][2];
ll da[20][100001][2],db[20][100001][2],dx,dy;
ll x0,n,m;
ll ga[N],gb[N];
bool comp(node a,node b){
	return a.h<b.h;
}
ll read(){
	ll sum=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0'){
		 if(ch=='-')f=-1;
		 ch=getchar();
	}
	while(ch<='9'&&ch>='0'){
		 sum=(sum<<3)+(sum<<1)+ch-'0';
		 ch=getchar();
	}
	return sum*f;
}
bool judge(ll l,ll m,ll r){
	if(!l)return 0;
	if(!r)return 1;
	return p[m].h-p[l].h<=p[r].h-p[m].h; 
}
ll dis(ll i,ll j){
	return abs(p[pos[i]].h-p[pos[j]].h);
}
void work(ll s,ll x){
	dx=0;dy=0;
	int k=0;
	for(int i=18;i>=0;i--)
	if(f[i][s][k]&&dx+dy+da[i][s][k]+db[i][s][k]<=x){
	    dx+=da[i][s][k];
		dy+=db[i][s][k];	
		if(i==0)k=1;
		s=f[i][s][k];
	}
}
int main(){	
//    freopen("drive.in","r",stdin);
//    freopen("drive.out","w",stdout);
    n=read();
    for(int i=1;i<=n;i++)
    {
     p[i].h=read();
     p[i].id=i;
	} 
	sort(p+1,p+n+1,comp);
	for(int i=1;i<=n;i++){
		p[i].l=i-1;
		p[i].r=i+1;
		pos[p[i].id]=i;
	}
	p[1].l=0;p[n].r=0;
	for(int i=1;i<=n;i++)
	{
		int j=pos[i],l=p[j].l,r=p[j].r;
		if(judge(l,j,r)){
			gb[i]=p[l].id;
			if(judge(p[l].l,j,r))ga[i]=p[p[l].l].id;
			else ga[i]=p[r].id;
		}
		else{
			gb[i]=p[r].id;
		   if(judge(l,j,p[r].r))ga[i]=p[l].id;
			else ga[i]=p[p[r].r].id;
		}
		if(l)p[l].r=r;
		if(r)p[r].l=l;
	}
	for(int i=1;i<=n;i++)
    {
    	if(ga[i]){f[0][i][0]=ga[i];da[0][i][0]=dis(i,ga[i]);} 
    	if(gb[i]){f[0][i][1]=gb[i];db[0][i][1]=dis(i,gb[i]);}
		da[0][i][1]=0;
		db[0][i][0]=0; 
	}
	int k;
    for(int i=1;i<=18;i++)
    for(int j=1;j<=n;j++)
    for(int t=0;t<=1;t++)
    {
      if(i==1)k=t^1;
      else k=t;
      if(f[i-1][j][t])f[i][j][t]=f[i-1][f[i-1][j][t]][k];
      if(f[i][j][t]){
      	da[i][j][t]=da[i-1][f[i-1][j][t]][k]+da[i-1][j][t];
       db[i][j][t]=db[i-1][f[i-1][j][t]][k]+db[i-1][j][t];
	  }
	}
	x0=read();m=read();
	double mini=inf;
	int ans;
	for(int i=1;i<=n;i++)
	{
      work(i,x0);
      if(dy&&(1.0*dx/dy)<mini)
      {
      mini=1.0*dx/dy;
      ans=i;
       }
	 }
	 cout<<ans<<endl;
	 int x,s;
	for(int i=1;i<=m;i++)
	 {
	   s=read();x=read();
	   work(s,x);
	   cout<<dx<<' '<<dy<<endl;
	 }
	return 0;
} 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值