【jzoj 7207】缘木求鱼(数论)(高精)

缘木求鱼

题目链接:jzoj 7207

题目大意

定义 f(x) 函数为开一个数组大小为 x 的线段树,它的最大下标。
要你求 l~r 范围内 f(x)/x 的最大值。

思路

首先几个东西要知道。

  1. 线段树一个点左子树大小最多比右子树大小大一。
  2. 深度大的点比深度小的点编号大,如果相同深度,右边的点大。
  3. 大小为 x 的线段树的深度是 log2x

那首先我们想如何求 f(x)
首先确定最终方向,由于线段树是有关二进制的,而且数据范围是二进制给入,还是几十万位,所以我们是要从它的二进制上来找一些结论。

首先是最暴力的,从 1 开始一步一步走,那我们考虑能不能直接确定最优的在那一边。
(假设当前区间长度为 x,左儿子区间长度为 x0=x+12,右儿子区间长度为 x1=x2
然后结合上面的第二条,我们可以得出当且仅当 log2x0>log2x1 时,最优值才会在左边。而且根据第一条,这个时候一定会有 x=2k+1(k1)

然后你会发现在出现最高的两个 1 之前都是往右走,不会满足 2k+1 条件。
然后到了之后,因为一直在满足(除了最后一步),所以就是一直左走,最后右走。

那我们发现 f(x) 只跟最高位的两个 1 有关系,如果设分别是 2p,2q(p>q),我们还可以得出:
f(x)=(2q+11)2pq2+1=2p+22pq+1+1

但是 x 不一定有两个 1 啊,如果只有一个 1 呢?
显然,那就是一直右走,设为 2p,那就是:
f(x)=2p+11

然后我们就可以把第三档的部分分拿了。


接下来,我们才可以开始这题的第二档暴力。(笑死)

我们枚举 p,qq 可能无),然后剩下的低位在 [l,r] 的前提下尽可能的小。

但是这时候又有问题了,我们要比较的是 f(x)x,做高精度小数除法是在想什么。

所以我们就考虑比较当前最优答案和当前要更新的答案。
f(x)x<f(y)y
f(x)y<f(y)x

然后由于 f(x) 只有两项或者三项,我们可以直接将其看做把 x / y 的二进制的两个 / 三个平移操作得到的二进制加起来,那就只有高精加和高精减了。

那计算 O(n),枚举 p,qO(n2),总的就是 O(n3),我们就能过掉前三档啦。


然后就是最后一档啦,那我们就来慢慢的优化吧。

然后由于 12i 的答案会比 12i1 的优,我们可以限制 max{n,m1}pm,然后就变成 O(n2) 的。

然后小小的证明:
你多了一层,你的点最大位置肯定会至少 2,那你数是最大乘了 2,所以结果只可能变大,不可能变小。

但是还是木大啊。


我们考虑减少决策点(q) 的选择。

首先我们保证 l,r 二进制位数相同,如果不同,我们可以把它分成两部分(前面说了位数可以变成至多只差一位),然后两个的答案取最优的。

然后我们把 l,r 用二进制表示出来。
l=10...01...
r=10...10...

那如果这里搞 LCP,LCP 里面有超过一个 1,那第二个 1 的范围就直接限死了,就直接是 l
那否则我们看第二个 1 放在哪里。

那假设两个的第二个一分别是 u,v。(r 找到的是 vl 找到的是 uuv
那我们的 q 就只能在 [u,v] 中,如果是 u,那就是 l,放在其它位置((u,v])的时候,后面的位都放 0

那除了 u,别的都可以以 x=2p+2q 的形式表示出来。
可以通过打表找规律和进行导数练习得到 p 固定的时候,q 接近 p/2 最优。
(你也可以自己用电脑画个图像看看)

然后你的决策点就变得很少。
(注意一些左端点在 p/2 右边,就只能选左端点;右端点在 p/2 左边,就只能选右端点)
(然后为了以防万一,我们会把 l 的答案也算上)
然后各种情况处理一下计算一下就好了。

然后决策点就只剩下两三个左右,就变成 O(n) 可以过啦。

代码

#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n, m, ansn; char L[2000001], R[2000001], mid[2000001]; int ans[2000001], ans_[2000001], tmp[2000001]; int x_[4000001], y_[4000001], opx[4], opy[4]; int degx[4], degy[4], xn, yn; void jia(int *x, int *y, int deg) { int len = deg + y[0], w = 0; for (int i = deg + 1; i <= len; i++) { x[i] += w + y[i - deg]; w = x[i] / 2; x[i] %= 2; } while (w) { x[++len] = w; w = x[len] / 2; x[len] /= 2; } if (x[0] < len) x[0] = len; } void jian(int *x, int *y, int deg) { int len = deg + y[0], w = 0; for (int i = deg + 1; i <= len; i++) { x[i] -= w + y[i - deg]; w = (x[i] < 0); x[i] &= 1; } while (w) { x[++len] -= w; w = (x[len] < 0); x[len] &= 1; } while (x[0] > 0 && !x[x[0]]) x[0]--; } void get_good(int *x, int *y) { if (x[0] == y[0]) {//相同 bool same = 1; for (int i = 1; i <= x[0]; i++) if (x[i] != y[i]) { same = 0; break; } if (same) return ; } for (int i = 1; i <= x_[0]; i++) x_[i] = 0; for (int i = 1; i <= y_[0]; i++) y_[i] = 0; x_[0] = y_[0] = 0; xn = yn = 0; int firx = x[0] + 1, firy = y[0] + 1; for (int i = 2; i <= x[0]; i++) if (x[x[0] - i + 1]) { firx = i; break; } for (int i = 2; i <= y[0]; i++) if (y[y[0] - i + 1]) { firy = i; break; } if (firx == x[0] + 1) {//2^0+2^1+...+2^p degx[++xn] = x[0]; opx[xn] = 1; degx[++xn] = 0; opx[xn] = -1; } else {//2^(p+2)-2^(p-q+1)+1 degx[++xn] = x[0] + 1; opx[xn] = 1; degx[++xn] = firx; opx[xn] = -1; degx[++xn] = 0; opx[xn] = 1; } if (firy == y[0] + 1) { degy[++yn] = y[0]; opy[yn] = 1; degy[++yn] = 0; opy[yn] = -1; } else { degy[++yn] = y[0] + 1; opy[yn] = 1; degy[++yn] = firy; opy[yn] = -1; degy[++yn] = 0; opy[yn] = 1; } for (int i = 1; i <= xn; i++) {//移位加减 if (opx[i] == 1) jia(x_, y, degx[i]); else jian(x_, y, degx[i]); } for (int i = 1; i <= yn; i++) { if (opy[i] == 1) jia(y_, x, degy[i]); else jian(y_, x, degy[i]); } if (x_[0] < y_[0]) {//比较,如果 y_ 优就交换 int maxn = max(x[0], y[0]); for (int i = 0; i <= maxn; i++) swap(x[i], y[i]); } else if (x_[0] == y_[0]) { for (int i = x_[0]; i >= 1; i--) { if (x_[i] > y_[i]) return ; if (x_[i] < y_[i]) { int maxn = max(x[0], y[0]); for (int i = 0; i <= maxn; i++) swap(x[i], y[i]); return ; } } } } void get_ans(char *l, char *r, int n) { int now = 1, onenum = 0; while (now <= n && l[now] == r[now]) onenum += (l[now++] == '1'); if (onenum > 1) {//lcp 超过 1 个 1 for (int i = 1; i <= n; i++) ans[n - i + 1] = l[i] - '0'; ans[0] = n; return ; } int lfir = n + 1, rfir = n + 1; for (int i = 2; i <= n; i++)//各自找到第二个 1 if (l[i] == '1') { lfir = i; break; } for (int i = 2; i <= n; i++) if (r[i] == '1') { rfir = i; break; } int p2 = n / 2 + 1; bool ltwo = 0; for (int i = 2; i <= n; i++) if (l[i] == '1') { ltwo = 1; break; } for (int i = 1; i <= n; i++)//先试一下选 l 的 ans[n - i + 1] = l[i] - '0'; ans[0] = n; if (rfir <= p2 && p2 <= lfir) {//可以选到 p/2 if (p2 == lfir && ltwo) {//会被 >=l 限制(选l)(前面处理了所以就不用再弄) if (rfir < lfir && lfir > 2) {//因为会限制所以考虑用两个 1 的方法选后面可以选的那个 for (int i = 1; i <= n; i++) tmp[i] = 0; tmp[n - (p2 - 1) + 1] = tmp[n - 1 + 1] = 1; tmp[0] = n; get_good(ans, tmp); } return ; } for (int i = 1; i <= n; i++)//普通的选 p/2 tmp[i] = 0; tmp[n - p2 + 1] = tmp[n - 1 + 1] = 1; tmp[0] = n; get_good(ans, tmp); return ; } else if (p2 > lfir) {//l 那边超了,限制 l for (int i = 1; i <= n; i++) tmp[i] = 0; if (ltwo) tmp[n - (lfir - 1) + 1] = 1; else tmp[n - lfir + 1] = 1; tmp[n - 1 + 1] = 1; tmp[0] = n; get_good(ans, tmp); return ; } else if (rfir > p2) {//r 那边超了 for (int i = 1; i <= n; i++) tmp[i] = 0; tmp[n - rfir + 1] = tmp[n - 1 + 1] = 1; tmp[0] = n; get_good(ans, tmp); return ; } } int main() { scanf("%d %s %d %s", &n, L + 1, &m, R + 1); if (n + 1 < m) {//直接处理最后两位的(1~2^(i+1) 答案比 1~2^i 的优) n = m - 1; L[1] = '1'; for (int i = 2; i <= n; i++) L[i] = '0'; } if (n + 1 == m) {//要拆成两个相同为的 for (int i = 1; i <= n; i++) mid[i] = '1'; get_ans(L, mid, n); swap(ans_, ans); mid[1] = '1'; for (int i = 2; i <= m; i++) mid[i] = '0'; get_ans(mid, R, m); get_good(ans, ans_); } else {//直接做 get_ans(L, R, n); } for (int i = 1; i <= ans[0]; i++)//这里倒叙储存,所以要倒叙输出(字符串是正序,数字的是倒叙) printf("%d", ans[ans[0] - i + 1]); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/jzoj_7207.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(30)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示