首页 > 其他分享 >P1081 [NOIP2012 提高组] 开车旅行

P1081 [NOIP2012 提高组] 开车旅行

时间:2022-08-31 12:44:46浏览次数:64  
标签:p2 旅行 le NOIP2012 ll p1 nex P1081 ct

记城市 \(i\) 的海拔高度为\(h_i\), \(i\) 和 \(j\) 之间的距离 \(d_{i,j}=|h_i-h_j|\)。

旅行过程中,两人轮流开车,第一天 \(A\) 开车,之后每天轮换一次。选择一个城市 \(s\) 作为起点,向编号大的一方行驶,并且最多行驶 \(x\) 公里就结束旅行。

\(B\) 总是沿着前进方向选择一个最近的城市作为目的地,而 \(A\) 总是沿着前进方向选择第二近的城市作为目的地(如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 \(x\) 公里,他们就会结束旅行。

1、 对于一个给定的 \(x=x_0\),从哪一个城市出发, \(\frac{A}{B}\) 最小(如果 \(B\) 的行驶路程为 \(0\),此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,\(A\) 开车行驶的路程总数与 \(B\) 行驶的路程总数的比值都最小,则输出海拔最高的那个城市。

2、对任意给定的 \(x=x_i\) 和出发城市 \(s_i\) , \(A\) 开车行驶的路程总数以及 \(B\) 行驶的路程总数。

\(1\le n,m \le 10^5\),\(-10^9 \le h_i≤10^9\),\(1 \le s_i \le n\),\(0 \le x_i \le 10^9\) , \(h_i\) 互不相同。


用 \(na[i],nb[i]\) 表示在 \(i\) 位置上下一步 \(A\) 和 \(B\) 的选择,可以倒序枚举,每次找到在 \(i\) 位置之前、之后的2个位置,就可以通过比较绝对值得到答案,这里用 multiset 来实现。

用 \(f[i][j]\) 表示 \(A,B\) 两人从 \(j\) 开始,每个人都开了 \(2^i\) 次之后到达的地方, \(na[i][j],nb[i][j]\) 表示 \(A,B\) 在 \(j\) 开始,开了 \(2^i\) 次后的总路程。那么每次询问从大到小跳倍增,若大于等于 \(x\) ,则更新,然后最后可能会剩下1轮 \(A\) 能走 \(B\) 不能走,这时候就再将 \(A\) 走一步即可。通过这样的方法可以解决2个问题。

#include<bits/stdc++.h>
using namespace std;

#define INF 1000000000000
#define ll long long
struct node
{
	ll id,h;
	friend bool operator < (node a,node b)
    {
        return a.h<b.h; 
    }
}ct[100005];

multiset <node> s;

ll n,m,na[100005],nb[100005],x0,f[20][100005],da[20][100005],db[20][100005];
ll la,lb,s0,ansa,ansb;

void solve(ll S,ll X)
{
	la=0,lb=0;
	for(int j=16;j>=0;--j)
	{
		if(f[j][S] && da[j][S]+db[j][S]<=X)
		{
			X-=da[j][S]+db[j][S];
			la+=da[j][S];
			lb+=db[j][S];
			S=f[j][S];
		}
	}
	if(na[S] && abs(ct[S].h-ct[na[S]].h)<=X)
		la+=abs(ct[S].h-ct[na[S]].h);
	return;
}

int main()
{
	//freopen("P1081.in","r",stdin);
	scanf("%lld",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%lld",&ct[i].h);
		ct[i].id=i;
	}
	ct[0].id=0;
	ct[0].h=-INF;
	ct[n+1].id=0;
	ct[n+1].h=INF;
	s.insert(ct[0]);
	s.insert(ct[0]);
	s.insert(ct[n+1]);
	s.insert(ct[n+1]);
	for(int i=n;i>=1;--i)
	{
		s.insert(ct[i]);
		set<node>::iterator p1=s.lower_bound(ct[i]);
		set<node>::iterator p2=p1;
		p1--;p2++;
		node las=(*p1),nex=(*p2);
		if(abs(las.h-ct[i].h)>abs(nex.h-ct[i].h))
		{
			nb[i]=nex.id;
			p2++;
		}
		else
		{
			nb[i]=las.id;
			p1--;
		}
		las=(*p1),nex=(*p2);
		if(abs(las.h-ct[i].h)>abs(nex.h-ct[i].h))
		{
			na[i]=nex.id;
			p2++;
		}
		else
		{
			na[i]=las.id;
			p1--;
		}
	}
	for(int i=1;i<=n;++i)
	{
		f[0][i]=nb[na[i]];
		if(na[i])
			da[0][i]=abs(ct[i].h-ct[na[i]].h);
		if(nb[na[i]])
			db[0][i]=abs(ct[na[i]].h-ct[nb[na[i]]].h);
	}
	for(int j=1;j<=16;++j)
	{
		for(int i=1;i<=n;++i)
		{
			f[j][i]=f[j-1][f[j-1][i]];
			da[j][i]=da[j-1][i]+da[j-1][f[j-1][i]];
			db[j][i]=db[j-1][i]+db[j-1][f[j-1][i]];
		}
	}
	scanf("%lld",&x0);
	ansa=1,ansb=0;
	for(int i=1;i<=n;++i)
	{
		solve(i,x0);
		if(la*ansb<lb*ansa)
		{
			ansa=la;
			ansb=lb;
			s0=i;
		}
		else if(la*ansb==lb*ansa && lb*ansa!=0 && ct[i].h>ct[s0].h)
		{
			ansa=la;
			ansb=lb;
			s0=i;
		}
	}
	printf("%lld\n",s0);
	scanf("%lld",&m);
	for(int i=1;i<=m;++i)
	{
		ll si,xi;
		scanf("%lld %lld",&si,&xi);
		solve(si,xi);
		printf("%lld %lld\n",la,lb);
	}
	return 0;
}

标签:p2,旅行,le,NOIP2012,ll,p1,nex,P1081,ct
From: https://www.cnblogs.com/zhouzizhe/p/16642697.html

相关文章

  • 「HNOI2019」校园旅行
    将相邻且颜色相同的点视作一个连通块,若该连通块是二分图,那么从连通块中一点\(x\)到连通块中一点\(y\)的路径的奇偶性确定所以对于块外一点\(x\)到块内一点\(y\),可以将它们......
  • 1026 [NOIP2001]Car的旅行路线 标点建图 勾股定理 floyd
     链接:https://ac.nowcoder.com/acm/contest/26077/1026来源:牛客网题目描述又到暑假了,住在城市A的Car想和朋友一起去城市B旅游。她知道每个......
  • 暑假集训五[星际旅行, 砍树, 超级树, 求和]
    暑假集训5星际旅行这个题刚看我觉得很ex,没事思路,就跳了,然后就去欺负\(T4\)了后来别的不会做,然后回来肝它...就肝出来了...对了,注意开\(longlong\)首先转化一下题意,我......
  • 牛的旅行
    P1522[USACO2.4]牛的旅行CowTours-洛谷|计算机科学教育新生态(luogu.com.cn)dfs为图中的连通块染色floyd求出任意两点的最短距离,不连通就INF求出每个连通块中......