对于一些dp问题,可以用倍增的思想来在nlogn的时间内求出问题的答案
若没有跑路器,那么直接Floyd做即可
考虑有跑路器的情况,有改变的只是所有距离为2的整数次幂的点的距离
先处理出来两个点之间有没有距离为2的整数次幂的路程,若有,让两个点的距离变成1,然后跑floyd即可
for(int k=1;k<=64;k++){
for(int i=1;i<=a;i++){
for(int j=1;j<=a;j++){
for(int o=1;o<=a;o++){
if(ok[o][i][k-1]==true&&ok[i][j][k-1]==true){
ok[o][j][k]=true;//如果左右两段都是true,则可以拼出这个长度
dis[o][j]=1;
}
}
}
}
}
不论求哪个子问题,都能发现需要处理出来每个点经过x天后所到达的点,然后枚举x天后的情况,判断合法性更新答案
考虑用二进制来做这样的工作,求出在第i个点,经过2^k天后所到达的点,到达这个点a车开的距离和b车开的距离
要想求出这个问题,需要三个数组
to[i][j][k]//在第j个点,i先开车,经过2^k天后所到达的点
la[i][j][k]//a所经过的距离
lb[i][j][k]//b所经过的距离
先处理出来k=0的情况
for(long long i=a;i>=1;i--){//最近一定在前驱/后继,次近一定在前驱/前驱的前驱/后继/后继的后继
s.insert(node(h[i],i));
set<node>::iterator it=s.lower_bound((node(h[i],i)));
it--;
long long w1=(*it).w,id1=(*it).id;
it++;it++;
long long w2=(*it).w,id2=(*it).id;
if(id1<0&&id2>a) continue;
if(h[i]-w1<w2-h[i]||(h[i]-w1==w2-h[i]&&h[id1]<h[id2])){
to[0][i][0]=id1;
la[0][i][0]=h[i]-w1;
it--;it--;it--;
long long w3=(*it).w,id3=(*it).id;
if(h[i]-w3<w2-h[i]||(h[i]-w3==w2-h[i]&&h[id3]<h[id2])){
to[1][i][0]=id3;
lb[1][i][0]=h[i]-w3;
}else{
to[1][i][0]=id2;
lb[1][i][0]=w2-h[i];
}
}else{
to[0][i][0]=id2;
la[0][i][0]=w2-h[i];
it++;
long long w3=(*it).w,id3=(*it).id;
if(h[i]-w1<w3-h[i]||(h[i]-w1==w3-h[i]&&h[id1]<h[id3])){
to[1][i][0]=id1;
lb[1][i][0]=h[i]-w1;
}else{
to[1][i][0]=id3;
lb[1][i][0]=w3-h[i];
}
}
}
然后利用倍增来推出其他情况的值
for(long long j=1;j<=a;j++){//2^1的情况
to[0][j][1]=to[1][to[0][j][0]][0];
to[1][j][1]=to[0][to[1][j][0]][0];
la[0][j][1]=la[0][j][0];
lb[0][j][1]=lb[1][to[0][j][0]][0];
la[1][j][1]=la[0][to[1][j][0]][0];
lb[1][j][1]=lb[1][j][0];
}
for(long long i=2;(1<<i)<=a;i++){//其他情况
for(long long j=1;j<=a;j++){
to[0][j][i]=to[0][to[0][j][i-1]][i-1];
la[0][j][i]=la[0][j][i-1]+la[0][to[0][j][i-1]][i-1];
lb[0][j][i]=lb[0][j][i-1]+lb[0][to[0][j][i-1]][i-1];
to[1][j][i]=to[1][to[1][j][i-1]][i-1];
la[1][j][i]=la[1][j][i-1]+la[1][to[1][j][i-1]][i-1];
lb[1][j][i]=lb[1][j][i-1]+lb[1][to[1][j][i-1]][i-1];
}
}
知道了所有情况到达的点和a,b两车所行驶的距离,就可以在log的复杂度计算出来每种问题的答案了
问题1:
for(long long i=1;i<=a;i++){
long long h1=0,h2=0,dis=b,p=i;
for(long long k=18;k>=0;k--){
if(la[1][p][k]+lb[1][p][k]<=dis&&to[1][p][k]>0&&to[1][p][k]<=a){
dis=dis-la[1][p][k]-lb[1][p][k];
h2+=la[1][p][k];
h1+=lb[1][p][k];
p=to[1][p][k];
}
}
if(h2==0) continue;
double qz=(double)h1/(double)h2;
if(hh>qz||(hh==qz&&h[ans1]<h[i])){
ans1=i;
hh=qz;
}
}
问题2和问题1类似,只用求一次就行了
while(c--){
long long o,k;
scanf("%lld%lld",&o,&k);
long long h2=0,h1=0;
for(long long ty=18;ty>=0;ty--){
if(la[1][o][ty]+lb[1][o][ty]<=k&&to[1][o][ty]>0&&to[1][o][ty]<=a){
k=k-la[1][o][ty]-lb[1][o][ty];
h2+=la[1][o][ty];
h1+=lb[1][o][ty];
o=to[1][o][ty];
}
}
printf("%lld %lld\n",h1,h2);
}