@bzoj - [email protected] [Poi2011]Lightning Conductor

目录

  • @[email protected]
  • @[email protected]
    • @part - [email protected]
    • @part - [email protected]
    • @part - [email protected]
  • @accepted [email protected]
    • @version - [email protected]
    • @version - [email protected]
  • @[email protected]

@[email protected]

已知一个长度为 n 的序列 a1, a2, ..., an。

对于每个1 <= i <= n,找到最小的非负整数 p 满足对于任意的 j,有:

\[a_j <= a_i + p - \sqrt{|i-j|}\]

input

第一行 n (1 <= n <= 500000)。

下面每行一个整数,其中第 i 行是 ai (0 <= ai <= 10^9)。

output

n行,第i行表示对于i,得到的p

sample input

6

5

3

2

4

2

4

sample output

2

3

5

3

5

4

@[email protected]

@part - [email protected]

我们先把式子变形:

\[a_j + \sqrt{|i-j|} - a_i <= p\]

求最小的 p 使得这个式子对于任何 j 都成立,相当于求 \(a_j + \sqrt{|i-j|}\) 的最大值。

我们可以分类处理 i < j 与 i > j 两种情况来去掉根号内的绝对值。现在仅讨论 i > j 的情况, i < j 的方法可以同理。

@part - [email protected]

那么怎么求这个东西的最大值呢?暴力做是 \(O(n^2)\) 的时间复杂度,好像也不能什么线段树斜率优化等诸如此类的优化来着。

这时候就要引入一个新方法:决策单调性。

决策单调性是这样定义的:对于 j < k,在点 i 处 k 比 j 优,则在 i 之后 k 会始终比 j 优。

或者等价的,令点 i 取到最优解的点(决策点)为 \(p_i\),则决策单调性为:\(p_i \le p_{i+1}\)。

如果具有决策单调性,可以利用先前我们决策时的信息,排除掉一些不可能是最优解的点,以降低时间复杂度。

那么这道题具有决策单调性吗?观察 \(y=\sqrt x\) 这一个函数,直观上它的增长率越来越小(事实上从它的导数来看的确是这样的),所以越靠前的点对后面的点的贡献会越来越小。所以决策单调性好像是成立的。

证明……恕我太弱证不来 QAQ。

【反正我选择打表,打出来单调就假装它是有决策单调性】

@part - [email protected]

假如我们已知这道题具有决策单调性,那我们怎么才能利用这个性质呢?

我已知的有两种方法:

(1)分治。根据决策单调性 \(p_i \le p_{i+1}\),我们进行分治处理。

对于原数列上的某一个区间 \([l,r]\),假设它对应的决策点区间为 \([L, R]\),我们取 \(l, r\) 的终点 \(mid\),暴力扫一遍 \([L, R]\) 求出 \(mid\) 对应的决策点 \(Mid\)。

则原问题分成两个规模更小的子问题:原区间 \([l, mid-1]\) 对应决策点区间 \([L, Mid-1]\),原区间 \([mid+1, r]\) 对应决策点区间 \([Mid+1, R]\)。分治即可。

时间复杂度 \(O(n\log n)\)。

该方法具有一定的局限性:决策点的选取前后不能有依赖性。即我们如果要求 \(mid\) 所对的决策点,不能依赖于求解 \(1\dots mid-1\)的决策点。然而大部分的 dp 是满足不了这个性质的。

(2)单调队列 + 二分。还是根据决策单调性 \(p_i \le p_{i+1}\),所以我们每次加入新的决策点时,它所能影响的区间肯定是从末尾开始依次往前影响。

于是我们维护一个队列,每一次加入新的决策点时就开始弹出队尾,直到新的决策点影响到的点全部更新。

然而这样有一个问题:假如点 j 影响到的区间为 [l, r],现在加入 k,它所能影响的区间为 [p, r]。则我们必须更新点 j 影响到的区间 [l, p-1]。

更新还好办,关键是怎么找这个 p。这个时候我们就可以根据决策单调性进行二分。

可以发现每次加入一个决策点,只需要我们二分一次。故时间复杂度 \(O(n\log n)\)。

该方法也具有一定的局限性:我们必须要支持快速求某一个决策点对另外一个点的贡献。否则这个方法的时间复杂度将快速退化。

@accepted [email protected]

@version - [email protected]

这个是单调栈 + 二分过的。

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 500000;
int a[MAXN + 5], pnt[MAXN + 5];
//pnt -> point -> 决策点 qwq
void reverse(int A[], int n) {
    for(int i=1,j=n;i<j;i++,j--)
        swap(A[i], A[j]);
}
struct node{
    int le, ri;
    int num;
    node(int _l=0, int _r=0, int _n=0):le(_l), ri(_r), num(_n){}
}que[MAXN + 5];
int s, t;
bool better(int i, int j, int k) {
    return a[j] - a[i] + sqrt(i - j) >= a[k] - a[i] + sqrt(i - k);
}
int b_search(node p, int x) {
    int le = p.le, ri = p.ri;
    while( le < ri ) {
        int mid = (le + ri + 1) >> 1;
        if( better(mid, p.num, x) ) le = mid;
        else ri = mid - 1;
    }
    return le;
}
void get_point(int n) {
    s = 1, t = 0;
    for(int i=1;i<=n;i++) {
        while( s <= t && que[s].ri < i )
            s++;
        if( s <= t && que[s].le < i )
            que[s].le = i;
        while( s <= t ) {
            if( better(que[t].ri, i, que[t].num) ) {
                if( better(que[t].le, i, que[t].num) )
                    t--;
                else {
                    que[t].ri = b_search(que[t], i);
                    break;
                }
            }
            else break;
        }
        if( s > t ) que[++t] = node(i, n, i);
        else if( que[t].ri != n )
            que[t+1] = node(que[t].ri+1, n, i), t++;
        if( s <= t ) pnt[i] = que[s].num;
    }
}
int ans[MAXN + 5];
int main() {
    int n; scanf("%d", &n);
    for(int i=1;i<=n;i++)
        scanf("%d", &a[i]);
    get_point(n);
    for(int i=1;i<=n;i++)
        ans[i] = max(ans[i], int(ceil(a[pnt[i]] - a[i] + sqrt(i - pnt[i]))));
    reverse(a, n); get_point(n); reverse(pnt, n); reverse(a, n);
    for(int i=1;i<=n;i++)
        ans[i] = max(ans[i], int(ceil(a[n-pnt[i]+1] - a[i] + sqrt(n-pnt[i]+1 - i))));
    for(int i=1;i<=n;i++)
        printf("%d\n", ans[i]);
}

@version - [email protected]

这个是用分治过的。

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 500000;
int a[MAXN + 5], pnt[MAXN + 5];
//pnt -> point -> 决策点 qwq
void reverse(int A[], int n) {
    for(int i=1,j=n;i<j;i++,j--)
        swap(A[i], A[j]);
}
bool better(int i, int j, int k) {
    return a[j] - a[i] + sqrt(i - j) >= a[k] - a[i] + sqrt(i - k);
}
void get_point(int l, int r, int L, int R) {
    if( l > r ) return ;
    int mid = (l + r) >> 1, nw = L;
    for(int i=L;i<=R&&i<=mid;i++)
        if( better(mid, i, nw) ) nw = i;
    pnt[mid] = nw;
    get_point(l, mid-1, L, nw);
    get_point(mid+1, r, nw, R);
}
int ans[MAXN + 5];
int main() {
    int n; scanf("%d", &n);
    for(int i=1;i<=n;i++)
        scanf("%d", &a[i]);
    get_point(1, n, 1, n);
    for(int i=1;i<=n;i++)
        ans[i] = max(ans[i], int(ceil(a[pnt[i]] - a[i] + sqrt(i - pnt[i]))));
    reverse(a, n); get_point(1, n, 1, n); reverse(pnt, n); reverse(a, n);
    for(int i=1;i<=n;i++)
        ans[i] = max(ans[i], int(ceil(a[n-pnt[i]+1] - a[i] + sqrt(n-pnt[i]+1 - i))));
    for(int i=1;i<=n;i++)
        printf("%d\n", ans[i]);
}

@[email protected]

平方根不能取整,不然会翻车(决策单调性不能成立)

当初做的时候,硬是要简化程序,把整个数组翻转过来再做。结果好多细节,WA 了几遍才过。

早知道我就直接复制粘贴了 TWT。

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10213838.html

时间: 2024-11-05 23:23:38

@bzoj - [email protected] [Poi2011]Lightning Conductor的相关文章

【BZOJ2216】[Poi2011]Lightning Conductor 决策单调性

[BZOJ2216][Poi2011]Lightning Conductor Description 已知一个长度为n的序列a1,a2,...,an.对于每个1<=i<=n,找到最小的非负整数p满足 对于任意的j, aj < = ai + p - sqrt(abs(i-j)) Input 第一行n,(1<=n<=500000)下面每行一个整数,其中第i行是ai.(0<=ai<=1000000000) Output n行,第i行表示对于i,得到的p Sample I

P3515 [POI2011]Lightning Conductor(决策单调性分治)

P3515 [POI2011]Lightning Conductor 式子可转化为:$p>=a_j-a_i+sqrt(i-j) (j<i)$ $j>i$的情况,把上式翻转即可得到 下面给一张图证明这是满足决策单调性的 把$a_j+sqrt(i-j)$表示在坐标系上 显然$sqrt(i-j)$的增长速度趋缓 曲线$a$被曲线$b$超过后是无法翻身的 对两个方向进行决策单调性分治,取$max$即可 #include<iostream> #include<cstdio>

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu

@bzoj - [email&#160;protected] 旅行规划

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 请你维护一个序列,支持两种操作: (1)某个区间 [x, y] 内的数同时加上一个增量 k. (2)询问某一个区间 [x, y] 中从 1 开始的最大前缀和. input 第一行给出一个整数 n.n <= 100000.接下来一行 n 个整数表示序列的初始值. 第三行给出一个整数 m,

@bzoj - [email&#160;protected] Hard Nim

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] n 堆石子,每堆石子的数量是不超过 m 的一个质数. 两个人玩 nim 游戏,问使后手必胜的初始局面有多少种. 模 10^9 + 7. input 多组数据.数据组数 <= 80. 每组数据一行两个正整数,n 和 m.1 <= n <= 10^9, 2 <= m <

@bzoj - [email&#160;protected] [Cqoi2016]伪光滑数

目录 @description@ @[email protected] @version - [email protected] @version - [email protected] @accepted [email protected] @[email protected] @description@ 若一个大于 1 的整数 M 的质因数分解有 k 项,其最大的质因子为 \(A_k\),并且满足 \(A_k^k \le N\),\(A_k < 128\),我们就称整数 M 为 N - 伪光

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

@bzoj - [email&#160;protected] [POI2015] Pustynia

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的正整数序列 a,每个数都在 1 到 10^9 范围内. 告诉你其中 s 个数,并给出 m 条信息,每条信息包含三个数 l, r, k 以及 k 个正整数,表示 a[l], a[l+1], ..., a[r-1], a[r] 里这 k 个数中的任意一个都比任意一个剩