【noip 2012】开车旅行 倍增+双端链表

17 篇文章 0 订阅
7 篇文章 0 订阅

听说 set会T,就用的是双向链表,结果还是卡着过得,rqnoj上数据太强了,其他oj都是秒切,最后还被迫加了读入优化,把倍增改成14才 过得。预处理前缀后缀用双向链表。set常数太大,和我的暴力得分一样,

连这玩意没听过的:BZOJ 1588营业额统计    我的题解:http://blog.csdn.net/pbihao/article/details/52831600

至于倍增嘛,其实很简单,把小A和小B走一轮(不是哪一个)作为一个循环节,然后倍增上去,再记录倍增后小A走的路程和小B走的,其实难在求前后缀。glk写的treap(对,手撕)结果没过,哈哈哈,不过我是没有那个心情的啦

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#define LL long long
#define maxn 100020
using namespace std;

int n,m,b[maxn],toa[maxn],tob[maxn],fa[maxn][20],fb[maxn][20],f[maxn][20],h[maxn];
struct edge{
	int ls,rs,id,h;
	bool operator<(const edge& b)const{return h<b.h;}
}e[maxn];
struct Pre{
	int dis,id,h;
	Pre(int a=0,int b=0,int c=0):dis(a),id(b),h(c){}
	bool operator < (const Pre& b)const {return dis==b.dis? h<b.h : dis < b.dis;}
}t[5];
void front(){
	sort(e+1,e+1+n);
	for(int i=1;i<=n;i++){
		b[e[i].id]=i;
		if(i^1)e[i].ls=i-1;
		else e[i].ls=0;
		if(i^n)e[i].rs=i+1;
		else e[i].rs=0;
	}
	
	for(int i=1;i<=n;i++){
		int now=0;
		int nw=b[i],l1=e[nw].ls,l2=e[l1].ls,r1=e[nw].rs,r2=e[r1].rs;
		if(l1!=0)t[++now]=Pre(abs(e[l1].h-e[nw].h),e[l1].id,e[l1].h);
		if(l2!=0)t[++now]=Pre(abs(e[l2].h-e[nw].h),e[l2].id,e[l2].h);
		if(r1!=0)t[++now]=Pre(abs(e[r1].h-e[nw].h),e[r1].id,e[r1].h);
		if(r2!=0)t[++now]=Pre(abs(e[r2].h-e[nw].h),e[r2].id,e[r2].h);
		if(l1^0)e[l1].rs=r1;
		if(r1^0)e[r1].ls=l1;
		sort(t+1,t+1+now);
		if(now>0)tob[i]=t[1].id;
		if(now>1)toa[i]=t[2].id;
	}
}

void read(int& x){
	char c=getchar();x=0;
	int flag=1;
	for(;(c>'9'||c<'0')&&c!='-';c=getchar());
	if(c=='-')flag=-1,c=getchar();;
	for(;c>='0'&&c<='9';c=getchar())x=x*10+c-'0';
	x*=flag;
}

void query(int st,int x,LL& a,LL& b){
	a=0,b=0;
	for(int i=14;i>=0;i--)if(f[st][i]&&fa[st][i]+fb[st][i]<=x){
		x-=fa[st][i]+fb[st][i];
		a+=fa[st][i],b+=fb[st][i];
		st=f[st][i];
	}
	if(toa[st]==0)return ;
	int dis=abs(h[toa[st]]-h[st]);
	if(dis<=x){
		a+=dis;
	}
}
int x0;
void solve(){
	read(x0);
	LL A,B,x,y;
	int ans=0;
	for(int i=1;i<=n;i++){
		query(i,x0,A,B);
		if(!ans||(B!=0&&(A*y<B*x))){
			ans=i,x=A,y=B;
		}
	}
	printf("%d\n",ans);
	read(m);
	int s,X;
	while(m--){
		read(s),read(X);
		query(s,X,A,B);
		printf("%lld %lld\n",A,B);
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		read(e[i].h);e[i].id=i;
		h[i]=e[i].h;
	}
	front();
	for(int i=1;i<=n;i++){
		int pos1=toa[i],pos2=tob[toa[i]];
		fa[i][0]=pos1 ? abs(h[pos1]-h[i]) : 0;
		fb[i][0]=pos2 ? abs(h[pos2]-h[pos1]) : 0;
		f[i][0]=pos2;
	}
	for(int j=1;j<=14;j++)
	for(int i=1;i<=n;i++)
	{
		f[i][j]= f[f[i][j-1]][j-1];
		fa[i][j] = fa[i][j-1] + fa[f[i][j-1]][j-1];
		fb[i][j] = fb[i][j-1] + fb[f[i][j-1]][j-1];
	}
	solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值