11.2晚校内测试装桶模拟单调栈

Posted wans-caesar-02111007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了11.2晚校内测试装桶模拟单调栈相关的知识,希望对你有一定的参考价值。

真的是fo了,晚上还来一次测试......


mister
【问题描述】 不久前 Mister 从太空中探测到一个奇怪的信号,他开始研究这个信号。 经过一些变换后,这个信号变成了长度为 n 的排列或者它的循环移位。对于进一步的研究 Mister 需要一些数据分析,这就是为什么他决定选择这个排列的循环移位,它有最小的可 能偏差。
我们把排列的偏差定义为技术分享图片

求一个可能偏差最小的排列 p 的循环移位。 让我们表示 k(0≤k < n)的循环移位排列 p 的变化需要达到这种转变,例如: k = 0: 变成 p1 p2 … pn, k = 1: 变成 pn p1 … pn - 1, …, k = n - 1: 变成 p2 p3 … pn p1。
【输入格式】 第一行包含单个整数 n(2≤n≤10^6)——排列的长度。 第二行包含 n 个空格分隔的整数 p1 p2 … pn(1≤pi≤n)的元素排列。 保证所有元素都是不同的。
【输出格式】 输出一个整数:排列 p 的循环移位的最小偏差。
【样例数据】 Input 3 2 3 1 Output 0 Input 3 3 2 1 Output 2
【数据范围】 10%的数据:n<=100 30%的数据:n<=2000 70%的数据:n<=50000 100%的数据:n<=1000000


 Solution

 一开始毫无头绪QAQ

发现规律,如果当前位置的值大于这个位置,那么每往后平移一步,答案会减少1,反之答案会增加1。以下把$p[i]-i$称为某一位的贡献,$p[i]-i>=0$称为贡献为正,反之称为贡献为负。

所以考虑怎么每次快速维护答案的增减量。如上诉,明显需要维护每平移一步贡献从正变成负和从负变成正的数的个数,开个桶可以$O(1)$处理出初始每个数需要多少步会改变符号,装到桶里表示经过$i$步有多少个数贡献会变负。

发现如果当前位置贡献为负,那么往后平移贡献会始终为负,除了最后一位跳到第一位可能会变化,进行特殊处理即可。

所以每次贡献为正的数量减去之前桶处理的,贡献为负的加上桶处理的,就是当前贡献为正和为负的数量。答案减去正的加上负的再更新最后一位的特判就是新的答案。

还有一些细节在代码中展示。

Code

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#define LL long long
using namespace std;

int p[2000005], cur[2000005];

int main() {
    int n;
    scanf("%d", &n);
    LL sum = 0, L = 0, R = 0, tmp = 0;
    for(int i = 1; i <= n; i ++) {
        scanf("%d", &p[i]);
        sum += abs(p[i] - i);
        if(p[i] >= i)    L ++, cur[p[i] - i] ++;
        else    R ++;
    }
    LL ans = sum;
    for(int i = 0; i < n - 1; i ++) {////i表示的是进行i+1次平移(方便下标写法 
        L -= cur[i];    R += cur[i];
        sum = sum - L + R - 1 - abs(p[n - i] - n) + p[n - i] - 1;//第n位贡献一定为负,所以要减去负的贡献中多算的一个 
        cur[p[n - i] + i] ++;//第一位贡献一定为正 
        L ++, R --;
        if(sum < ans)    ans = sum, tmp = i + 1;
    }
    printf("%lld %d
", ans, tmp);
    return 0;
}

array
【问题描述】 给你一个由 n 个元素组成的数组。这个数组的某个连续子序列的不平衡值是这个段中最大 和最小元素的差值。数组的不平衡值是该数组所有连续子序列不平衡值的总和。 例如,数组[1,4,1]的失衡值为 9,因为这个数组有 6 个不同的子段: [1](从 a1 到 a1),失衡值为 0; [1,4](从 a1 到 a2),失衡值为 3; [1,4,1](从 a1 到 a3),失衡值为 3; [4](从 a2 到 a2),失衡值为 0; [4,1](从 a2 到 a3),失衡值为 3; [1](从 a3 到 a3),失衡值为 0; 你需要确定数组 a 的不平衡值。
【输入格式】 第一行包含一个整数 n(1≤n≤10^6)数组的长度。 第二行包含 n 个整数 a1 a2 … an (1≤ai≤10^6)——的元素数组。
【输出格式】 输出一个整数: a 数组的不平衡值。
【样例数据】 Input 3 1 4 1 Output 9
【数据范围】 10%的数据:n<=100 30%的数据:n<=5000 70%的数据:n<=10^5 100%的数据:n<=10^6


Solution

看起来好单调栈!!但是为什么仔细一想好不可做??

实际上就是单调栈....

答案就等于$sum{a[i]*(以a[i]为最大值的区间个数-以a[i]为最小值的区间个数}$QAQ,仔细想想是不是好有道理QAQAQAQ

Code

#include<bits/stdc++.h>
using namespace std;

inline int read() {
    int x = 0, t = 0; char ch = getchar();
    while(!isdigit(ch))    t |= (ch == -), ch = getchar();
    while(isdigit(ch)) x = x * 10 + ch - 0, ch = getchar();
    return x *= (t ? -1 : 1);
}

int l[1000005], r[1000005], a[1000005], stk[1000005], top, n;
long long ans;
int main() {
    freopen("array.in", "r", stdin);
    freopen("array.out", "w", stdout);
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)    a[i] = read();
    for(int i = 1; i <= n; i ++)    l[i] = r[i] = i;
    for(int i = 1; i <= n; i ++) {
        while(top && a[i] >= a[stk[top]]) {
            r[stk[top]] = i - 1;
            top --;
        }
        l[i] = stk[top] + 1;
        stk[++top] = i;
    }
    while(top) {
        r[stk[top]] = n;
        top --;
    }
    for(int i = 1; i <= n; i ++)    ans += 1ll * a[i] * (i - l[i] + 1) * (r[i] - i + 1);
    for(int i = 1; i <= n; i ++)    l[i] = r[i] = i;    top = 0;
    for(int i = 1; i <= n; i ++) {
        while(top && a[stk[top]] >= a[i]) {
            r[stk[top]] = i - 1;
            top --;
        }
        l[i] = stk[top] + 1;
        stk[++top] = i;
    }
    while(top) {
        r[stk[top]] = n;
        top --;
    }
    for(int i = 1; i <= n; i ++)    ans -= 1ll * a[i] * (i - l[i] + 1) * (r[i] - i + 1);
    printf("%lld", ans);
    return 0;
}

 

以上是关于11.2晚校内测试装桶模拟单调栈的主要内容,如果未能解决你的问题,请参考以下文章

[CSP-S模拟测试]:array(单调栈)

csp-s模拟测试50(9.22)「施工(单调栈优化DP)」·「蔬菜(二维莫队???)」·「联盟(树上直径)」

模拟初级计算器 单调栈实现

用数组模拟栈 队列 以及单调栈 单调队列应用

用数组模拟栈 队列 以及单调栈 单调队列应用

2022年蓝桥杯第二次校内选拔赛