缘木求鱼

题目链接:jzoj 7207

题目大意

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

思路

首先几个东西要知道。

  1. 线段树一个点左子树大小最多比右子树大小大一。
  2. 深度大的点比深度小的点编号大,如果相同深度,右边的点大。
  3. 大小为 x 的线段树的深度是 ⌈ log ⁡ 2 x ⌉ \left\lceil\log_2^x\right\rceil ⌈log2x​⌉

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

首先是最暴力的,从 1 1 1 开始一步一步走,那我们考虑能不能直接确定最优的在那一边。
(假设当前区间长度为 x x x,左儿子区间长度为 x 0 = ⌊ x + 1 2 ⌋ x0=\left\lfloor\dfrac{x+1}{2}\right\rfloor x0=⌊2x+1​⌋,右儿子区间长度为 x 1 = ⌊ x 2 ⌋ x1=\left\lfloor\dfrac{x}{2}\right\rfloor x1=⌊2x​⌋)
然后结合上面的第二条,我们可以得出当且仅当 ⌈ log ⁡ 2 x 0 ⌉ > ⌈ log ⁡ 2 x 1 ⌉ \left\lceil\log_2^{x0}\right\rceil>\left\lceil\log_2^{x1}\right\rceil ⌈log2x0​⌉>⌈log2x1​⌉ 时,最优值才会在左边。而且根据第一条,这个时候一定会有 x = 2 k + 1 ( k ⩾ 1 ) x=2^k+1(k\geqslant1) x=2k+1(k⩾1)。

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

那我们发现 f ( x ) f(x) f(x) 只跟最高位的两个 1 1 1 有关系,如果设分别是 2 p , 2 q ( p > q ) 2^p,2^q(p>q) 2p,2q(p>q),我们还可以得出:
f ( x ) = ( 2 q + 1 − 1 ) ∗ 2 p − q ∗ 2 + 1 = 2 p + 2 − 2 p − q + 1 + 1 f(x)=(2^{q+1}-1)*2^{p-q}*2+1=2^{p+2}-2^{p-q+1}+1 f(x)=(2q+1−1)∗2p−q∗2+1=2p+2−2p−q+1+1

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

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


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

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

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

所以我们就考虑比较当前最优答案和当前要更新的答案。
f ( x ) x < f ( y ) y \frac{f(x)}{x}<\frac{f(y)}{y} xf(x)​<yf(y)​
f ( x ) y < f ( y ) x f(x)y<f(y)x f(x)y<f(y)x

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

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


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

然后由于 1 ∼ 2 i 1\sim 2^i 1∼2i 的答案会比 1 ∼ 2 i − 1 1\sim 2^{i-1} 1∼2i−1 的优,我们可以限制 max ⁡ { n , m − 1 } ⩽ p ⩽ m \max\{n,m-1\}\leqslant p\leqslant m max{n,m−1}⩽p⩽m,然后就变成 O ( n 2 ) O(n^2) O(n2) 的。

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

但是还是木大啊。


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

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

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

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

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

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

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

然后决策点就只剩下两三个左右,就变成 O ( n ) O(n) 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^pdegx[++xn] = x[0]; opx[xn] = 1;degx[++xn] = 0; opx[xn] = -1;}else {//2^(p+2)-2^(p-q+1)+1degx[++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 个 1for (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/2tmp[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;
}

【jzoj 7207】缘木求鱼(数论)(高精)相关推荐

  1. 【高精】Gift(jzoj(gz) 1763)

    Gift jzoj(gz) 1763 题目大意: 有9个数:a,b,c--i,计算出2a+2b+--2h+i2^a+2^b+--2^h+i2a+2b+--2h+i 输入样例 1 21 30 0 0 0 ...

  2. 【DP】【高精】WZK打雪仗(jzoj 1997)

    WZK打雪仗 jzoj 1997 题目大意: 在一个环上有n*2个点,问有多少种连法可以用n条线连接成n对点 输入样例 5 输出样例 42 解释: 一种可行的方案如下: 数据范围 对于30%数据: n ...

  3. 【DP】【高精】逆序对(jzoj 2014)

    逆序对 jzoj 2014 题目大意: 有一个长为n的序列(由1,2,3,--n组成),问经过某种调整之后,有k个逆序对(即在前面的一个数大于后面的一个数这样的对)的种数,有多组数据,以0 0结尾 样 ...

  4. 【高精】Oliver的成绩(jzoj 2008)

    Oliver的成绩 题目大意: Oliver考了一次试,现在知道他的语数英的成绩,还有年级其他n个人的成绩,现在问Oliver三科各和年级第一差多少分,如果Oliver在这一科上是第一,则输出'0' ...

  5. 【DP】【高精】幸运票 (jzoj 2122)

    幸运票 题目大意: 一个长度为2N的序列,这些数的总和为S,当这个序列的前N个和后N个总和相等时,它是符合题意的,问有符合题意的有多少种可能 样例输入 2 2 样例输出 4 数据范围限制 1<= ...

  6. 【卡特兰数】【递推】【高精+压位】JZOJ·WZK打雪仗

    题目大意: 有2n个人站在一个圈里,有n人要去其他n人那(两人连一线),线不能与其他线碰在一起,让你求方案数. 思路: 卡特兰数,因为位数大,高精压位走起 CodeCodeCode: #include ...

  7. 【JZOJ】【卡特兰数】【高精】WZK打雪仗

    DescriptionDescriptionDescription 按顺序排成的一个有2n个点的圆,问有多少种方法,连接n条边(两个点之间相连),每个点只能连一条边,问有多少种方案使得n条边互不相交 ...

  8. 高精地图与自动驾驶(下)

    高精地图与自动驾驶(下) 二.高精地图与自动驾驶 谈到高精地图对自动驾驶的作用,可以对比自动驾驶和人类驾驶的流程的相通性. 人驾驶的时候以眼睛为主,耳朵辅助观察测量环境:自动驾驶车用多种传感器包括摄像 ...

  9. 高精地图与自动驾驶(上)

    高精地图与自动驾驶(上) 前言 自动驾驶的实现主要有三个步骤:感知.决策规划.行车控制.这与你走路上班/上学的逻辑是相似的:眼睛看到画面,告诉大脑,然后你就知道了自己在哪里,以及要往哪个方向走,并指挥 ...

最新文章

  1. javascript数字千分位格式化
  2. 计算机动画火柴人作业,(Flash期末作品综合实验报告.doc
  3. 如何查看静态库和动态库是32位还是64位
  4. SQL Server中的锁的简单学习
  5. CentOS 7 下安装 Redis
  6. Git学习总结(21)——Git 提交规范总结
  7. gtx1050ti最稳定的驱动_【硬件资讯】持续霸榜经久不衰?四岁高龄的GTX1060仍为Steam最受欢迎显卡!...
  8. python简单的爬虫实例
  9. 诺基亚N8手机的导航功能
  10. armeabi armeabi-v7a mips x86 理解
  11. Python爬取链家成都二手房源信息 asyncio + aiohttp 异步爬虫实战
  12. linux解密shadow_Linux系统中的/etc/shadow文件超详细内容解析
  13. 鸿蒙时代实力排名,混沌氏(浑沌)、鸿蒙氏,盘古开天辟地时两个最强大的部落首领?...
  14. built a JNCIS LAB系列:Chapter 4 BGP
  15. 新疆维吾尔自治区坡度数据
  16. 树形表实现 bootstrap-table + treegrid
  17. 云服务器选股,【图】老师们能不能在通达信7.48中整合云行情服务器?_选股公式,股票,炒股公式,股票指标,股票论坛_股票软件技术交流论坛_理想论坛 - 股票论坛...
  18. CTFshow击剑杯osint——人家想玩嘛,人有点多超详细解法
  19. ts中的interface与type的区别
  20. 江苏科技大学计算机科学与工程,江苏科技大学

热门文章

  1. 115网盘离线下载升级,支持下载115任何用户分享文件的提取
  2. Android UC浏览器使用痕迹分析
  3. 造成死机的硬件有哪些?CPU风扇正常的转速是多少?
  4. mysql for python 安装_MySQLdb for python 安装
  5. 勒让德符号的说明及作用
  6. 猫主子总往家里带老鼠?那就用深度学习打造一个「猫门」吧
  7. 于新年伊始(后篇):回首而宣誓
  8. Linux学习之入门篇(一)
  9. PHP调用kaldi,程序员罗杰-JAVA 调用PHP Webservices
  10. wamp下配置php文件,wamp开发环境快速搭建详细步骤