【NOIP2012提高组】开车旅行

把ab各走一次算一步
f[i][j]表示i点跳2^j步走到的点
da[i][j]表示i点跳2^j步a走的路程
db[i][j]表示i点跳2^j步b走的路程

  1. 倒着扫一遍求从i点a走出一次走到的点toi,记录da[i][0]。然后,我们把to[i]和i连边
    2.倒着扫一遍求从i ,b走出一次走到的点t,再枚举所有i的出边,设边的另一端为p,则p走“一步”就是走到了t了,记录f[p][0]。同理,记录db。
  2. 倍增,注意可能最后a可以多走一步。
  3. 。。。
  4. 这道题主要是初始化难,其他随便搞搞调调都行了。
#include<bits/stdc++.h>
#define N 100010
#define inf 2147483647
#define ll long long
#define point(a) multiset<a>::iterator 
#define mod (ll)(1e9+7)
#define mem(a,b) memset(a,b,sizeof (a))
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout);
using namespace std;


struct rel
{ll v,fr;}e[N];

ll n,i,j,k,t,m,disa,disb,ans,tot;
ll H[N],to[N],tt[N],f[N][20],da[N][20],db[N][20],s,x,tail[N];
//to -a 
struct node
{
	ll id,h;
	bool operator < (const node b) const
	{
		node a=*this;
		return a.h== b.h ? H[a.id] < H[b.id] : a.h < b.h;
	}
};

multiset<node> a;
long double rat,cmp;

void redouble()
{
	for(j=1;j<20;j++)
		for(i=1;i<=n;i++)
		{
			if(f[i][j-1] == n) continue;
			f[i][j] = f[f[i][j-1]][j-1];
			da[i][j] = da[i][j-1] + da[f[i][j-1]][j-1];
			db[i][j] = db[i][j-1] + db[f[i][j-1]][j-1];
		}
	return ;
}

void add(ll u,ll v)
{
	e[++tot].v=v;
	e[tot].fr=tail[u];
	tail[u]=tot;
}
void choose_go(char p , ll s,ll t,ll d)
{
	if(p=='a')
	{
		da[s][0]=d;
		to[s]=t;
		add(t,s);
		
	}else
	{
		for(ll p=tail[s];p;p=e[p].fr)
		{
			db[e[p].v][0]=d;
			f[e[p].v][0]=t;
		}
	}
}
void init()
{
	a.clear();
	tot=0;
	mem(to,0); mem(tail,0); mem(da,0); mem(db,0); mem(f,0);
	for(i=n;i>0;i--)
	{
		a.insert((node){i,H[i]});
		point(node) it = a.find((node){i,H[i]}),l=it,r=it;
		
		point(node) rbegin = a.end(),begin = a.begin(),end=a.end(); 	--rbegin;
		
		for(ll j=1;j<=2;j++)
		{
			if(l!=begin) -- l;
			if(r!=rbegin) ++ r;
		}
		++r;
		node x,y;
		x=y=(node){inf,inf};
		for(point(node) p =l;p!=r;++p)if(p!=it)
		{
			node now=(node){p->id , abs(p->h - H[i])};
			if(now < x)
			{
				y=x;
				x=now;
			}else if(now < y) y=now;
		}
		//to , pre , da 
		if(y.id!=inf)choose_go('a',i,y.id,y.h);
	}
	a.clear();
	for(i=n;i>0;i--)
	{
		a.insert((node){i,H[i]});
		point(node) it = a.find((node){i,H[i]}),l=it,r=it;
		--l,++r;
		ll decx = it == a.begin() ? inf : H[i] - l->h;
		ll decy = r == a.end() ? inf : r->h - H[i];
		if(decx == inf && decy == inf)continue;
		if(decx < decy || decx==decy ) choose_go('b',i,l->id,decx); else if(decx > decy) choose_go('b',i,r->id,decy); 
		//***
	}
	redouble();
	return ;
	
}



void drive(ll s,ll x)
{
	disa=disb=0;
	ll p=s;
	for(ll i=19;i>=0;i--) if(f[p][i]) if(disa + disb + da[p][i] + db[p][i]<=x)
	{
		disa += da[p][i];
		disb += db[p][i];
		p= f[p][i];
	}
	if(to[p]) if(disa + disb + da[p][0] <=x) disa += da[p][0];
	return ;
}
int  main()
{
	scanf("%lld\n",&n);
	for(i=1;i<=n;i++)scanf("%lld",&H[i]);
	init();
	scanf("%lld",&x);
	cmp=inf;
	for(i=1;i<=n;i++)
	{
		drive(i,x);
		if(disb!=0) rat=(long double)disa / disb; else rat = inf-1;
		if(rat < cmp)
		{
			cmp=rat;
			ans=i;
		}else
		if(rat == cmp)
		{
			if(H[i] > H[ans]) ans=i;
		}
	}
	printf("%lld\n",ans);
	scanf("%lld",&m);
	while(m--)
	{
		scanf("%lld%lld",&s,&x);
		drive(s,x);
		printf("%lld %lld\n",disa,disb);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值