倍增专题
本蒟蒻只会个倍增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\n", 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\n", ans);
m = read();
for(i = 1; i <= m; i++)
{
S = read();
X = read();
ask(S);
printf("%d %d\n", int(A), int(B));
}
return 0;
}

浙公网安备 33010602011771号