NOIP2012开车旅行

这种题考试一定要先打暴力保证50分的暴力分。。。

调试得想吐。。。

最小和次小,倒着跑一边map或set就好了

询问为倍增

qq[i][j]表示从i城市跑2^j轮达到的城市

l[i][j][1]表示从i城市跑2^j轮A的行程

r[i][j][1]表示从i城市跑2^j轮B的行程

转移就好了

#include<cstring>
#include<set>
#include<cmath>
#include<map>
#define For(i,j,k) for(long long i=j;i<=k;i++)
#define Forr(i,j,k) for(long long i=j;i>=k;i--)
using namespace std;
const long long N=100000+5;
const long long inf=50000000000000ll;
map<long long ,long long>q;
map<long long ,long long>::iterator it;
long long h[N],next1[N],next2[N];
long long n,m;
double eps =0.0000001;
int sgn(double x,double y){
	if(fabs(x-y)<eps)return 0;
	if(x<y)return 1;
		return -1;
	}
inline long long read(){
	char c=getchar();
	long long f=1;
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	long long x=c-'0';
	c=getchar();
	while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
	return f*x;
	}
struct node{
	long long a;
	long long b;
	}temp[5];
long long compare(const node w1,const node w2){
	if(w1.a==w2.a)
	return w1.b<w2.b;
	return w1.a<w2.a;
	}
long long compare2(const long long a,const long long b){return a>b;}
void deal(){
	Forr(i,n,1){
		q[h[i]]=i;
		it=q.upper_bound(h[i]);
	    For(j,1,4)temp[j].a=inf;
		if(it!=q.end()){
			temp[1].a=abs(h[i]-it->first);temp[1].b=it->first;
			it++;
			if(it!=q.end())
			temp[2].a=abs(h[i]-it->first);temp[2].b=it->first;
			}
		it=q.lower_bound(h[i]);
			if(it!=q.begin()){
				it--;
				temp[3].a=abs(h[i]-it->first);temp[3].b=it->first;
				if(it!=q.begin()){
					it--;
				temp[4].a=abs(h[i]-it->first);temp[4].b=it->first;
					}
				}
	       sort(temp+1,temp+5,compare);
			it=q.lower_bound(temp[1].b);
		   if(temp[1].a!=inf)next1[i]=it->second;
			it=q.lower_bound(temp[2].b);
		   if(temp[2].a!=inf)next2[i]=it->second;
	   }
	}
long long qq[N][25],l[N][25][2];
void pre(){
	For(i,1,n)
	For(j,0,20)
	qq[i][j]=-1;
	For(i,1,n)
	if(next1[next2[i]]){
	qq[i][0]=next1[next2[i]];
	l[i][0][1]=abs(h[next2[i]]-h[i]);
	l[i][0][2]=abs(h[next1[next2[i]]]-h[next2[i]]);
	}
	For(j,1,20)
	For(i,1,n)
	if(qq[i][j-1]!=-1){
		qq[i][j]=qq[qq[i][j-1]][j-1];
		l[i][j][1]=l[i][j-1][1]+l[qq[i][j-1]][j-1][1];
		l[i][j][2]=l[i][j-1][2]+l[qq[i][j-1]][j-1][2];
		}
}
void solve1(){
	long long tot=read(),k=0;h[0]=-inf;
	double ans=2000000005.0;
	For(i,1,n){
	long long x=i,sum1=0,sum2=0;
         Forr(j,20,0)
	           if(qq[x][j]!=-1&&sum1+sum2+l[x][j][1]+l[x][j][2]<=tot){
				    sum1+=l[x][j][1];
					sum2+=l[x][j][2];
				   x=qq[x][j];
		        }	
		           if(sum1+sum2+abs(h[next2[x]]-h[x])<=tot)sum1+=abs(h[next2[x]]-h[x]);
					if(sum2==0&&ans==2000000005.0){if(h[i]>h[k])k=i;}
					else if(sum2==0)k=k;
				  else if(sgn(sum1*1.0/sum2,ans)==1){ans=sum1*1.0/sum2;k=i;}
				  else if(!sgn(sum1*1.0/sum2,ans))if(h[i]>h[k])k=i;
		}

	cout<<k<<endl;
}
void solve2(long long x,long long y){
	long long tot=y;
	long long sum1=0,sum2=0;
	Forr(j,20,0)
		if(qq[x][j]!=-1){
			sum1+=l[x][j][1];
			sum2+=l[x][j][2];
			if(sum1+sum2>tot){sum1-=l[x][j][1];sum2-=l[x][j][2];continue;}
			x=qq[x][j];
			}
	if(sum1+sum2+abs(h[next2[x]]-h[x])<=tot)sum1+=abs(h[next2[x]]-h[x]);
	cout<<sum1<<" "<<sum2<<endl;
}
int main(){
	n=read();
	For(i,1,n)h[i]=read();
	deal();
	pre();
	solve1();
    m=read();
	long long x,y;
	For(i,1,m){x=read();y=read();solve2(x,y);}
	return 0;
	}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值