NOIp2012 开车旅行

传送门

以后序列上的问题可以想一想倍增。。
s a [ i ] / s b [ i ] sa[i]/sb[i] sa[i]/sb[i]记录在 i i i这个位置让 A / B A/B A/B开车到达的点。

A A A B B B都跳一次称为一轮。
d i s [ i ] [ j ] dis[i][j] dis[i][j]表示从 i i i 2 j 2^j 2j轮的总路程。
p o s [ i ] [ j ] pos[i][j] pos[i][j]表示从 i i i 2 j 2^j 2j轮后的位置。
d i s _ a [ i ] [ j ] dis\_a[i][j] dis_a[i][j]表示从 i i i 2 j 2^j 2j A A A所走的总路程。
B B B的路程用总路程减去 A A A的路程即可。

最近点和次近点用 s e t set set查一查,要注意距离相同时按照海拔从小到大排。

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
#define pii std::pair<int,int>
#define mp std::make_pair
#define se second
#define fi first

cs int N=1e5+10,oo=1e9+7,Log=17;
namespace IO{
	cs int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;}
	template <typename T>
	inline T get(){
		char ch=gc();T x=0,f=1;
		while(!isdigit(ch)){if(ch=='-')f=0;ch=gc();}
		while(isdigit(ch)) x=((x+(x<<2))<<1)+(ch^48),ch=gc();
		return f?x:-x;
	}
	inline int gi(){return get<int>();}
	inline ll gl(){return get<ll>();}
}
using IO::gi;
using IO::gl;
inline int min(int x,int y){return x<y?x:y;}
inline void Min(int &x,int y){if(x>y)x=y;}

int n,m,sa[N],sb[N],pos[N][Log],ans1=1;
pii h[N];ll dis_a[N][Log],dis_b[N],dis[N][Log],x_0;
double quo;ll suma,sumb;
std::set<pii> S;
std::set<pii>::iterator It;
inline int pre(cs pii &H){
	It=S.lower_bound(H);
	return It==S.begin()?0:(*(--It)).se;
}
inline int suf(cs pii &H){
	It=S.upper_bound(H);
	return It==S.end()?0:(*It).se;
}
std::vector<pii> x;
inline bool cmp(cs pii &a,cs pii &b){
	return a.fi==b.fi?h[a.se].fi<h[b.se].fi:a.fi<b.fi;
}
int main(){
//	freopen("1817.in","r",stdin);
	n=gi();
	for(int re i=1;i<=n;++i) h[i].fi=gi(),h[i].se=i;
	for(int re i=n,pos1,pos2,sz;i>=1;--i){
		S.insert(h[i]),sa[i]=sb[i]=0,x.clear();
		pos1=pre(h[i]),pos2=suf(h[i]);
		if(pos1) x.push_back(mp(h[i].fi-h[pos1].fi,pos1)),pos1=pre(h[pos1]);
		if(pos2) x.push_back(mp(h[pos2].fi-h[i].fi,pos2)),pos2=suf(h[pos2]);
		if(pos1) x.push_back(mp(h[i].fi-h[pos1].fi,pos1));
		if(pos2) x.push_back(mp(h[pos2].fi-h[i].fi,pos2));
		sz=x.size(),sort(x.begin(),x.end(),cmp);
		if(sz>=1) sb[i]=x[0].se;if(sz>=2) sa[i]=x[1].se;
		if(sa[i]) dis_a[i][0]=x[1].fi;if(sb[i]) dis_b[i]=x[0].fi;
		if(pos[i][0]=sb[sa[i]]) dis[i][0]=dis_a[i][0]+dis_b[sa[i]];
	}
	
	for(int re j=1;(1<<j)<=n;++j)
		for(int re i=1;i<=n;++i) if(pos[pos[i][j-1]][j-1])
			pos[i][j]=pos[pos[i][j-1]][j-1],
			dis[i][j]=dis[pos[i][j-1]][j-1]+dis[i][j-1],
			dis_a[i][j]=dis_a[pos[i][j-1]][j-1]+dis_a[i][j-1];
	
	x_0=gl(),quo=oo;
	for(int re i=1,now;i<=n;++i){
		ll X=x_0;suma=sumb=0,now=i;double sum=oo;
		for(int re j=Log-1;~j;--j) if(pos[now][j]&&dis[now][j]<=X)
				X-=dis[now][j],suma+=dis_a[now][j],sumb+=dis[now][j]-dis_a[now][j],now=pos[now][j];
		if(sa[now]&&dis_a[now][0]<=X) suma+=dis_a[now][0];
		if(sumb) sum=1.0*suma/sumb;
		if((sum<quo)||(sum==quo&&h[i].fi>h[ans1].fi)) quo=sum,ans1=i;
	}printf("%d\n",ans1);
		
	m=gi();
	for(int re i=1;i<=m;++i){
		int now=gi();ll go=gl();suma=sumb=0;
		for(int re j=Log-1;~j;--j) if(pos[now][j]&&dis[now][j]<=go)
				go-=dis[now][j],suma+=dis_a[now][j],sumb+=dis[now][j]-dis_a[now][j],now=pos[now][j];
		if(sa[now]&&dis_a[now][0]<=go) suma+=dis_a[now][0];
		printf("%lld %lld\n",suma,sumb);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值