我们先用set预处理出,在每一个城市,A和B下一次分别要去的地方
那么是求对于距离每个城市最小的和次小距离的其他城市。
由于set的自动有序性,我们可以先找到位于城市的指针,然后找这个指针左边的两个,右边的两个,最小和次小距离一定在这四个数之中。
然后一个一个点模拟会T掉。。。我们可以设小A,小B各开一次车为一轮, f(i,j) f ( i , j ) 表示在第i个城市,开 2j 2 j 轮车会到的城市, da(i,j) d a ( i , j ) 表示小A从第i个城市开始,经历了 2j 2 j 轮,小A开车的距离, db(i,j) d b ( i , j ) 同理
倍增的核心思想是
2i−1+2i−1=2i
2
i
−
1
+
2
i
−
1
=
2
i
所以可得状态转移方程:
da和db的方程是这样推出来的:由于那个核心思想,我们想要从i走 2j 2 j 步,就可以先走 2j−1 2 j − 1 步,再走 2j−1 2 j − 1 步,因此就有了 da(i,j−1)+da(f(i,j−1),j−1) d a ( i , j − 1 ) + d a ( f ( i , j − 1 ) , j − 1 ) 。其中 f(i,j−1) f ( i , j − 1 ) 表示从i走 2j−1 2 j − 1 步会到达的城市。
关于倍增的预处理代码:
for(int i=1; i<=n; i++) {
int f1 = fa[i];
int f2 = fb[fa[i]];
if(f1!=0) {
da[i][0] = dda[i];
}
if(f2!=0) {
db[i][0] = ddb[f1];
}
f[i][0] = f2;
}
for(int j=1; j<=16; j++)
for(int i=1; i<=n; i++) {
f[i][j] = f[f[i][j-1]][j-1];
da[i][j] = da[i][j-1] + da[f[i][j-1]][j-1];
db[i][j] = db[i][j-1] + db[f[i][j-1]][j-1];
}
}
另外…set的find操作有点慢,但set自带一个lower_bound(),速度大体上快于find
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
using namespace std;
#define debug(x) cerr<<#x<<"="<<x<<endl;
const int MAXN = 100010, MAXM = 10010;
const int INF =0x7fffffff - 10;
int x0,xi,si,n,m,h[MAXN],sta[MAXN],stb[MAXN],cnt;
int fa[MAXN], fb[MAXN],dda[MAXN],ddb[MAXN];
int f[MAXN][20],da[MAXN][20],db[MAXN][20];
struct City{
int id, h;
bool operator < (const City &a) const{
return h < a.h;
}
}c[MAXN];
struct temp{
int d,id;
bool operator <(const temp &a) const {
if(a.d != d)
return d < a.d;
else
return c[id].h < c[a.id].h;
}
}tem[6];
set <City> s;
set <City> :: iterator it;
void init() {
for(int i=n; i>=1; i--) {
cnt = 0;
s.insert(c[i]);
it = s.lower_bound(c[i]);//这里本来是find函数,后来我才发现可以进一步优化
if(it!=s.begin()) {
it--;
tem[++cnt] = (temp) {abs((it->h) - c[i].h),it->id};
if(it!=s.begin()) {
it--;
tem[++cnt] = (temp) {abs((it->h) - c[i].h),it->id};
it++;
}
it++;
}
if((++it)!=s.end()) {
tem[++cnt] = (temp) {abs((it->h) - c[i].h),it->id};
if((++it)!=s.end()) {
tem[++cnt] = (temp) {abs((it->h) - c[i].h),it->id};
}
}
sort(tem+1,tem+cnt+1);
if(tem[1].id) {
fb[i] = tem[1].id;
ddb[i] = tem[1].d;
}
if(tem[2].id) {
fa[i] = tem[2].id;
dda[i] = tem[2].d;
}
}
for(int i=1; i<=n; i++) {
int f1 = fa[i];
int f2 = fb[fa[i]];
if(f1!=0) {
da[i][0] = dda[i];
}
if(f2!=0) {
db[i][0] = ddb[f1];
}
f[i][0] = f2;
}
for(int j=1; j<=16; j++) {
for(int i=1; i<=n; i++) {
if(f[f[i][j-1]][j-1]) {
f[i][j] = f[f[i][j-1]][j-1];
da[i][j] = da[i][j-1] + da[f[i][j-1]][j-1];
db[i][j] = db[i][j-1] + db[f[i][j-1]][j-1];
}
}
}
}
double work1(int s, int x) {
long long xa = 0,xb = 0;
for(int i=16; i>=0; i--) {
if(f[s][i]&&da[s][i]+db[s][i] <= x) {
xa += da[s][i];
xb += db[s][i];
x -= da[s][i] + db[s][i];
s = f[s][i];
}
}
if(fa[s]&&da[s][0]<=x){
xa += da[s][0];
}
if(xb == 0) return (double)INF;
return double(xa)/double(xb);
}
void work2(int s, int x) {
long long xa = 0, xb = 0;
for(int i=16; i>=0; i--) {
if(f[s][i] && da[s][i] + db[s][i] <= x) {
xa += da[s][i];
xb += db[s][i];
x-= da[s][i] + db[s][i];
s = f[s][i];
}
}
if(fa[s]&&da[s][0]<=x) {
xa += da[s][0];
}
printf("%lld %lld\n", xa,xb);
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; i++) {
scanf("%d", &c[i].h);
c[i].id = i;
}
init();
scanf("%d", &x0);
double ans1 = INF+1,tem_ans;
int ansid;
for(int i=1; i<=n; i++) {
tem_ans = work1(i,x0);
if(tem_ans<ans1||(tem_ans==ans1 && c[i].h>c[ansid].h)) {
ans1 = tem_ans;
ansid = i;
}
}
printf("%d\n",ansid);
scanf("%d", &m);
for(int i=1; i<=m; i++) {
scanf("%d %d",&si, &xi);
work2(si,xi);
}
return 0;
}