本蒟蒻只会个倍增lca,实在太菜了。
稍微灵活一下的倍增就不会了,所以开一个倍增专题,先把倍增练熟
1.跑路
由每次走 2k 米很容易想到倍增。
map[k][i][j]表示从i走2k米能否走到 j
如果 map[k-1][i][l]==1 && map[k-1][l][j] == 1,那么map[k][i][j] == 1
如果 map[k][i][j]==1 那么就可以一次走过去,dis[i][j]=1
然后floyd求最短路(其他方法也行,数据小,且floyd好写)
#include <cstdio> #include <cstring> #include <iostream> #define N 101 #define min(x, y) ((x) < (y) ? (x) : (y)) int n, m; int map[N][N][N], dis[N][N]; inline int read() { int x = 0, f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; return x * f; } int main() { int i, j, k, l; n = read(); m = read(); memset(dis, 127 / 3, sizeof(dis)); for(i = 1; i <= m; i++) map[0][read()][read()] = 1; for(l = 1; l <= 64; l++) for(k = 1; k <= n; k++) for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) if(map[l - 1][i][k] && map[l - 1][k][j]) map[l][i][j] = 1; for(k = 0; k <= 64; k++) for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) if(map[k][i][j]) dis[i][j] = 1; for(k = 1; k <= n; k++) for(i = 1; i <= n; i++) for(j = 1; j <= n; j++) dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); printf("%d ", dis[1][n]); return 0; }
可以用一个队列来求出每个点第k远的点(神奇)
然后倍增搞,但是MLE,需要滚动数组。
#include <cstdio> #include <iostream> #define N 1000001 #define LL long long int n, k; LL m, a[N]; int f[2][N], ans[N]; int main() { int i, j, l, r; scanf("%d %d %lld", &n, &k, &m); for(i = 1; i <= n; i++) scanf("%lld", &a[i]); f[0][1] = k + 1; l = 1, r = k + 1; for(i = 2; i <= n; i++) { while(r < n && a[i] - a[l] > a[r + 1] - a[i]) l++, r++; f[0][i] = a[i] - a[l] >= a[r] - a[i] ? l : r; } for(i = 1; i <= n; i++) ans[i] = i; for(i = 1; m; i++, m >>= 1) for(j = 1; j <= n; j++) { if(m & 1) ans[j] = f[(i & 1) ^ 1][ans[j]]; f[i & 1][j] = f[(i & 1) ^ 1][f[(i & 1) ^ 1][j]]; } for(i = 1; i <= n; i++) printf("%d ", ans[i]); return 0; }
3.开车旅行
noip超恶心倍增题。
预处理出来小A和小B在每个位置所到的点,这个可以从后往前扫,将每一个扫到的数放到一个集合里面,因为集合可以自动排好序。
然后对于i,小A和小B能到的点只有可能是排好序后的i-1,i-2,i+1,i+2。
开一些倍增数组,因为小A和小B轮流开车,如果开两个倍增数组,一个表示小A,一个表示小B,那么倍增的时候要考虑奇偶性,非常麻烦,所以精妙的地方就来了。
把A走一次和B走一次合起来,算作一步,开个倍增数组f[N][21],另开两个倍增数组disA[N][21],disB[N][21]表示A和B走2k步的距离。
对于每一个询问倍增求解即可。
#include <set> #include <cstdio> #include <iostream> #include <algorithm> #define N 100001 #define abs(x) ((x) < 0 ? -(x) : (x)) using namespace std; double val = ~(1 << 31), A, B; int n, m, tmp, cnt, ans = 1, X, S; int f[N][21], disA[N][21], disB[N][21], a[N], b[N]; struct node { int height, id; bool operator < (const node &a) const { return height < a.height; } }h[N + 4]; set <node> s; set <node> :: iterator it; inline int read() { int x = 0, f = 1; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; return x * f; } inline bool cmp(node x, node y) { return abs(x.height - tmp) < abs(y.height - tmp) || (abs(x.height - tmp) == abs(y.height - tmp) && x.height < y.height); } inline double ask(int x) { int i, y = X; A = 0, B = 0; for(i = 20; i >= 0; i--) if(disA[x][i] + disB[x][i] <= y && f[x][i]) y -= disA[x][i] + disB[x][i], A += disA[x][i], B += disB[x][i], x = f[x][i]; if(disA[x][0] <= y) A += disA[x][0]; if(B == 0) return ~(1 << 31); return A / B; } int main() { int i, j; double x; n = read(); for(i = 1; i <= n; i++) { h[i].id = i; h[i].height = read(); } for(i = n; i >= 1; i--) { s.insert(h[i]); it = s.find(h[i]); cnt = 1; tmp = (*it).height; if(it != s.begin()) { it--; h[n + cnt++] = *it; if(it != s.begin()) { it--; h[n + cnt++] = *it; it++; } it++; } if(++it != s.end()) { h[n + cnt++] = *it; if(++it != s.end()) h[n + cnt++] = *it; } std::sort(h + n + 1, h + n + cnt, cmp); if(cnt > 1) b[i] = h[n + 1].id; if(cnt > 2) a[i] = h[n + 2].id; } for(i = 1; i <= n; i++) if(a[i]) { disA[i][0] = abs(h[i].height - h[a[i]].height); if(b[a[i]]) f[i][0] = b[a[i]], disB[i][0] = abs(h[a[i]].height - h[b[a[i]]].height); } for(j = 1; j <= 20; j++) for(i = 1; i <= n; i++) f[i][j] = f[f[i][j - 1]][j - 1], disA[i][j] = disA[i][j - 1] + disA[f[i][j - 1]][j - 1], disB[i][j] = disB[i][j - 1] + disB[f[i][j - 1]][j - 1]; X = read(); for(i = 1; i <= n; i++) { x = ask(i); if(x < val || (val == x && h[ans].height < h[i].height)) ans = i, val = x; } printf("%d ", ans); m = read(); for(i = 1; i <= m; i++) { S = read(); X = read(); ask(S); printf("%d %d ", int(A), int(B)); } return 0; }