LOJ #6435. 「PKUSC2018」星际穿越(倍增)

题面

LOJ#6435. 「PKUSC2018」星际穿越

题解

**参考了 这位大佬的博客 **

这道题好恶心啊qwq~~

首先一定要认真阅读题目 !! 注意 li<ri<xi 这个条件 !!

所以它询问的就是向左走的最短路了 .

不难发现只有两种策略 , 要么一直向左走 ; 要么第一次向右走 , 然后一直向左走 .

并且到一个定点 x 的最短路长度 肯定是从右向左一段段递增的 .

 为什么呢 ? 不难发现 如果向右走两次 , 那么只有一次是一定有效的 , 另外一次的 li 一定不会小于这次 .

向左走的话 , 每次就记下沿途的 li 的最小值 , 用这个去转移走 j 次时 l 的最小值就行了 . ( 70pts 见我 LOJ 提交吧qwq .)

然后这样暴力做的话就是 O(n2) 的复杂度 显然不行 .

考虑优化 , 发现这个是一段段的 且 有连续性 , 有一个神奇的倍增可以快速实现这个功能 .

fi,j[i,n] 所有点走 2j 次能到达的最左端点 .

fi,j=ffi,j1,j1

为什么要这样记呢 ? 因为这样可以同时统计第一次向右走可能产生的贡献 .

sumi,ji(ifi,j) 中所有点走的步数之和 . 这个转移就很显然啦 .

sumi,j=sumi,j1+sumfi,j1,j1+(fi,j1ffi,j1,j1)2j1

然后我们考虑走的时候算答案 . 因为我们一开始预处理只包括了可能向右走的情况 , 但直接第一步向左走的没有处理掉 .

此处我们单独处理第一步向左走的情况就行了 .

Calc(i,pos)i[i,pos) 的所需步数之和 . 那么每次询问就能用差分来表示成 Calc(l,pos)Calc(r+1,pos) 了 .

然后倍增的时候类似于这样跳的 :

img

假设我们总共要经过的是 红色 那一段(其中 lpos 已经跳完了) , 每次走的是 粉色 那一段 .

发现我们每次走的时候 , 要记下前面走了多少步数 , 然后给答案加上这一段的贡献 len×tot .

最后有一小段多余的 蓝紫色 (因为每次 2j 覆盖的是所有步数为这么多的 , 最后可能不满) 这段贡献就是 len×(tot+1) .

代码好像很短 ?

代码

#include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i) #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i) #define Set(a, v) memset(a, v, sizeof(a)) #define debug(a) cout << #a << ": " << a << endl using namespace std; inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;} inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;} inline int read() { int x = 0, fh = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1; for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); return x * fh; } void File() { #ifdef zjp_shadow freopen ("6435.in", "r", stdin); freopen ("6435.out", "w", stdout); #endif } const int N = 3e5 + 1e3, inf = 0x3f3f3f3f; int L[N], n, f[N][21], sum[N][21], Log2[N]; void Init() { f[n + 1][0] = inf; Fordown (i, n, 1) f[i][0] = min(f[i + 1][0], L[i]), sum[i][0] = i - f[i][0]; For (j, 1, Log2[n]) For (i, 1, n) if (f[i][j - 1]) { f[i][j] = f[f[i][j - 1]][j - 1]; sum[i][j] = sum[i][j - 1] + sum[f[i][j - 1]][j - 1] + ((f[i][j - 1] - f[i][j]) << (j - 1)); } } inline int Calc(int qp, int pos) { if (L[pos] <= qp) return pos - qp; int res = pos - L[pos]; pos = L[pos]; int tot = 1; Fordown (i, Log2[pos], 0) if (f[pos][i] > qp) res += sum[pos][i] + (pos - f[pos][i]) * tot, pos = f[pos][i], tot += 1 << i; return res + (pos - qp) * (tot + 1); } int main () { File(); n = read(); L[1] = 1; For (i, 2, n) L[i] = read(), Log2[i] = Log2[i >> 1] + 1; Init(); int m = read(); For (i, 1, m) { int l = read(), r = read(), x = read(); int ans = Calc(l, x) - Calc(r + 1, x), len = r - l + 1, g = __gcd(ans, len); printf ("%d/%d\n", ans / g, len / g); } return 0; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9186437.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(925)  评论(3编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示