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); } }