https://loj.ac/problem/2604
题目
两个人开车,A向后选择距离第二小的城市,B向后选择距离最小的城市,A先开,两个人轮流开一次,当达不到要求或者长度超过x以后就不开了
1:输入x0,求A开的距离与B开的距离比值最小时,出发的城市是什么(B的距离为0时视为正无穷,正无穷相等)
2:输入一系列x和出发城市,问A开了多长距离,B开了多长距离
城市有$10^5$个,两个城市之间的距离小于等于$2 imes10^9$,$xleqslant 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
", ch+1);
REP(i,0,m) {
int a,b; getd(s[i]-1, x[i], a, b);
printf("%d %d
", a, b);
}
}