NOIP2012提高组开车旅行
Posted alan_cty
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NOIP2012提高组开车旅行相关的知识,希望对你有一定的参考价值。
Description
现在有n个城市,每个城市有它的高度
Hi
,保证每个
Hi
互不相同。我们定义两个城市之间的距离
disi,j=|Hi−Hj|
,并且只能从编号小的城市去到编号大的城市。现在有两个人,小A和小B要开车(雾)去旅行。小A先开一天,小B再开一天。每一天都可以从一个开到另一个城市。小A会选择去离当前城市第二近的城市,小B会选择去离当前城市最近的那个城市。如果他们行驶的总路程将会超过给定的X就会不继续开车(∩_∩),结束旅行。求:
1:给定一个X,求从哪一个城市出发,小A行驶的路程/小B行驶的路程最小。(认为一个数/0=∞)。若有多个城市相等,选择高度最高的那个。
2:给出m个询问,每次询问从S出发,限制为X,小A走的路程和小B走的路程。
Solution
观察题目,一个很明显的想法就是把小A和小B从每个点出发将要去到的城市预处理出来。这一个东西可以用双向链表啦~线段树啦~平衡树啦~并查集啦~ set啦 ……等等这些很神奇的东西。(我打线段树预处理打了60行(TAT),Howar Li表示并查集大法好,不超过30行(QAQ))。
然后,你就可以打倍增了。嗯。就是这么简单。
设f[i][j][0]表示从城市i出发,小A经过2^j轮(注意是轮)之后走的路程。
f[i][j][1]表示小B走的路程
g[i][j]表示走到那个城市。
转移很明显(只要你会打RMQ),嗯。
然后对于第一个询问,暴力枚举起点即可。
对于第二个询问,直接倍增即可。
是不是很简单!?
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
#define db double
#define ll long long
#define inf 0x7fffffff
using namespace std;
struct node
int d;ll v;
p[N];
struct note
int mx,mi;
t[N*5];
bool cmp(node x,node y)
return x.v<y.v||x.v==y.v&&x.d<y.d;
db sum,ans;
int n,m,tot,x,y,k;
int h[N],a[N],b[N],w[N],g[N][18];
ll f[N][18][2],ana,anb,v[N];
void change(int v,int l,int r,int x)
if (l==r) t[v].mx=t[v].mi=l;return;
int m=(l+r)/2;
if (x<=m) change(v*2,l,m,x);
else change(v*2+1,m+1,r,x);
t[v].mx=max(t[v*2].mx,t[v*2+1].mx);
t[v].mi=min(t[v*2].mi,t[v*2+1].mi);
int getmx(int v,int l,int r,int x,int y)
if (x>y) return 0;
if (l==x&&r==y) return t[v].mx;
int m=(l+r)/2;
if (y<=m) return getmx(v*2,l,m,x,y);
else if (x>m) return getmx(v*2+1,m+1,r,x,y);
else return max(getmx(v*2,l,m,x,m),getmx(v*2+1,m+1,r,m+1,y));
int getmi(int v,int l,int r,int x,int y)
if (x>y) return n+1;
if (l==x&&r==y) return t[v].mi;
int m=(l+r)/2;
if (y<=m) return getmi(v*2,l,m,x,y);
else if (x>m) return getmi(v*2+1,m+1,r,x,y);
else return min(getmi(v*2,l,m,x,m),getmi(v*2+1,m+1,r,m+1,y));
void solve(int x,int y)
ana=anb=0;
fd(j,17,0)
if (f[x][j][0]+f[x][j][1]<=y)
y-=f[x][j][0]+f[x][j][1];
ana+=f[x][j][0];anb+=f[x][j][1];
x=g[x][j];
if (f[x][0][0]<=y) ana+=f[x][0][0];
int main()
scanf("%d",&n);
fo(i,1,n) scanf("%lld",&v[i]),p[i].v=v[i],p[i].d=i;
sort(p+1,p+n+1,cmp);v[0]=inf;
fo(i,1,n) h[p[i].d]=++tot,w[tot]=p[i].d;
fo(i,1,n*5) t[i].mi=n+1;
fd(i,n,1)
p[1].d=getmi(1,1,n,h[i]+1,n);p[2].d=getmx(1,1,n,1,h[i]-1);
p[3].d=getmi(1,1,n,p[1].d+1,n);p[4].d=getmx(1,1,n,1,p[2].d-1);
fo(j,1,4) p[j].v=abs(v[i]-v[w[p[j].d]]);
sort(p+1,p+5,cmp);
if (p[1].d!=0&&p[1].d!=n+1) b[i]=w[p[1].d];
if (p[2].d!=0&&p[2].d!=n+1) a[i]=w[p[2].d];
change(1,1,n,h[i]);
fo(i,1,n)
g[i][0]=b[a[i]];
f[i][0][0]=abs(v[i]-v[a[i]]);
f[i][0][1]=abs(v[a[i]]-v[b[a[i]]]);
fo(j,1,17)
fo(i,1,n)
g[i][j]=g[g[i][j-1]][j-1];
f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0];
f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1];
scanf("%d",&x);ans=inf;
fo(i,1,n)
solve(i,x);
if (!anb) sum=inf;else sum=ana*1.0/anb;
if (sum<ans||sum==ans&&v[i]>v[k]) ans=sum,k=i;
printf("%d\\n",k);
for(scanf("%d",&m);m;m--)
scanf("%d%d",&x,&y);
solve(x,y);
printf("%lld %lld\\n",ana,anb);
以上是关于NOIP2012提高组开车旅行的主要内容,如果未能解决你的问题,请参考以下文章