NOIP 2012 开车旅行

https://loj.ac/problem/2604

题目

两个人开车,A向后选择距离第二小的城市,B向后选择距离最小的城市,A先开,两个人轮流开一次,当达不到要求或者长度超过x以后就不开了

1:输入x0,求A开的距离与B开的距离比值最小时,出发的城市是什么(B的距离为0时视为正无穷,正无穷相等)

2:输入一系列x和出发城市,问A开了多长距离,B开了多长距离

城市有$10^5$个,两个城市之间的距离小于等于$2\times10^9$,$x\leqslant 10^9$

题解

如果不管数据范围,很容易设da[s][l][x]为从s出发,开了l次,第一次是x开的,A开的距离,同理设db[s][l][x]

然后很容易转移,最后O(n)循环找出符合条件的次数

然而这个l就超了,连状态都无法保存

由于l是单调递增的,每次转移都增加1,因此可以改为倍增

提前处理出开了$2^k$后到达的城市编号,然后设da[s][k][x]为从s出发,开了$2^k$次,第一次是x开的,A开的距离,同理设db[s][k][x]

思维比较简单,但是写起来很容易写错

AC代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define REP(i,a,b) for(register int i=(a); i<(b); i++)
#define REPE(i,a,b) for(register int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(register int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
int n, m;
struct node {
	int hb, id;
	bool operator<(const node &r) const {return hb<r.hb;}
} cs[100007];
struct pii {
	int a,b;
	bool operator<(const pii&r) const {
		if(b==-1) return false;
		return a<r.a || (a==r.a && cs[b].hb<cs[r.b].hb);
	}
};
int goa[100007], gob[100007];
int to[100007];
int nxt[100007], prv[100007];
inline void lk(int a, int b) {
	if(~a) nxt[a]=b;
	if(~b) prv[b]=a;
}
inline void gen() {
	sort(cs,cs+n);
	REP(i,0,n) to[cs[i].id]=i;
	REP(i,0,n) nxt[i]=i+1;
	REP(i,0,n) prv[i]=i-1;
	nxt[n-1]=-1;
	pii t[4], p;
	REP(i,0,n-1) {
		int now=to[i];
		p.a=0x7f7f7f7f; p.b=-1;
		int tn=0;
		if(~prv[now]) {
			p.a=cs[now].hb-cs[prv[now]].hb;
			p.b=prv[now];
			t[tn++]=p;
			int now2=prv[now];
			if(~prv[now2]) {
				t[tn].a=cs[now].hb-cs[prv[now2]].hb;
				t[tn].b=prv[now2];
				tn++;
			}
		}
		if(~nxt[now]) {
			pii tmp;
			tmp.a=cs[nxt[now]].hb-cs[now].hb;
			tmp.b=nxt[now];
			if(tmp<p) p=tmp;
			t[tn++]=tmp;
			int now2=nxt[now];
			if(~nxt[now2]) {
				t[tn].a=cs[nxt[now2]].hb-cs[now].hb;
				t[tn].b=nxt[now2];
				tn++;
			}
		}
		gob[i]=cs[p.b].id;
		if(tn>=2) {
			sort(t,t+tn);
			goa[i]=cs[t[1].b].id;
		} else {
			goa[i]=-1;
		}
		lk(prv[now], nxt[now]);
	}
	goa[n-1]=gob[n-1]=-1;
}
int x0, s[100007], x[int(1e5)+7]; //因为少写了个0错得很惨
int f[100007][32][2];
int da[100007][32][2], db[100007][32][2];
inline void genf() {
/*	f[i][2^0][0]=go[i];
	f[i][2^0][1]=go2[i];
	k=1 f[i][2^1][0]=f[f[i][2^0][0]][2^0][1];
	k>1 f[i][2^k][0]=f[f[i][2^(k-1)][0]][2^(k-1)][0];*/
	REP(i,0,n) f[i][0][0]=goa[i], f[i][0][1]=gob[i];
	REP(i,0,n) {
		if(~f[i][0][0]) f[i][1][0]=f[f[i][0][0]][0][1];
		else f[i][1][0]=-1;
		if(~f[i][0][1]) f[i][1][1]=f[f[i][0][1]][0][0];
		else f[i][1][1]=-1;
	}
	REP(k,2,32) REP(i,0,n) {
		if(~f[i][k-1][0]) f[i][k][0]=f[f[i][k-1][0]][k-1][0];
		else f[i][k][0]=-1;
		if(~f[i][k-1][1]) f[i][k][1]=f[f[i][k-1][1]][k-1][1];
		else f[i][k][1]=-1;
	}
}
inline void dp() {
	REP(i,0,n) {
		//其实-1不是特别有必要,因为只有可以到达才会使用dp
		if(~goa[i]) da[i][0][0]=abs(cs[to[goa[i]]].hb-cs[to[i]].hb); else da[i][0][0]=-1;
		da[i][0][1]=0;
		db[i][0][0]=0;
		if(~gob[i]) db[i][0][1]=abs(cs[to[gob[i]]].hb-cs[to[i]].hb); else db[i][0][1]=-1;
	}
	REP(i,0,n) {
		da[i][1][0]=da[i][0][0];
		if(~gob[i]) da[i][1][1]=da[gob[i]][0][0]; else da[i][1][1]=-1;
		if(~goa[i]) db[i][1][0]=db[goa[i]][0][1]; else db[i][1][0]=-1;
		db[i][1][1]=db[i][0][1];
	}
	REP(k,2,32) REP(i,0,n) {
		if(~f[i][k-1][0]) {
			da[i][k][0]=da[i][k-1][0]+da[f[i][k-1][0]][k-1][0];
			db[i][k][0]=db[i][k-1][0]+db[f[i][k-1][0]][k-1][0];
		} else da[i][k][0]=-1, db[i][k][0]=-1;

		if(~f[i][k-1][1]) {
			da[i][k][1]=da[i][k-1][1]+da[f[i][k-1][1]][k-1][1];
			db[i][k][1]=db[i][k-1][1]+db[f[i][k-1][1]][k-1][1];
		} else da[i][k][1]=-1, db[i][k][1]=-1;
	}
}
void getd(int s, int l, int &a, int &b) {
	int x=0; a=0, b=0;
	
	PERE(i,31,0) {
		if(~f[s][i][x] && da[s][i][x]+db[s][i][x]<=l) {
			l-=da[s][i][x]+db[s][i][x];
			a+=da[s][i][x], b+=db[s][i][x];
			s=f[s][i][x];
		}
	}
}
int main() {
	scanf("%d", &n);
	REP(i,0,n) scanf("%d", &cs[i].hb), cs[i].id=i;
	scanf("%d", &x0);
	scanf("%d", &m);
	REP(i,0,m) scanf("%d%d", &s[i], &x[i]);

	gen();
	genf();
	dp();
	int aa=-1,bb=-1,ch=-1;
	REP(i,0,n) {
		int a,b;
		getd(i,x0,a,b);

		if(aa<0) {
			aa=a,bb=b;
			ch=i;
		} else {
			long long s1=(long long)a*bb, s2=(long long)aa*b;
			if(s1<s2) {
				aa=a, bb=b, ch=i;
			}
		}

	}
	printf("%d\n", ch+1);
	REP(i,0,m) {
		int a,b; getd(s[i]-1, x[i], a, b);
		printf("%d %d\n", a, b);
	}
}

猜你喜欢

转载自www.cnblogs.com/sahdsg/p/12527780.html