【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\n", 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;
}

原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9903531.html

时间: 2024-10-11 07:56:25

【11.2晚校内测试】【装桶模拟】【单调栈】的相关文章

POJ 3250 Bad Hair Day 模拟单调栈

Bad Hair Day Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 14989   Accepted: 4977 Description Some of Farmer John's N cows (1 ≤ N ≤ 80,000) are having a bad hair day! Since each cow is self-conscious about her messy hairstyle, FJ wants

2017.11.25【NOIP提高组】模拟赛A组

2017.11.25[NOIP提高组]模拟赛A组 T1 3467. [NOIP2013模拟联考7]最长上升子序列(lis) T2 3468. [NOIP2013模拟联考7]OSU!(osu) T3 3472. [NOIP2013模拟联考8]匹配(match) T1 有转移方程f[i]=max{f[j]}+1,a[j]<a[i] 可以用线段树+离散化维护这个方程,因为涉及以往状态可以用主席树维护 打太丑爆空间了 Code 1 #include<cstdio> 2 #include<c

C++11:移动构造函数的测试

C++11:移动构造函数的测试 代码如下: #include <iostream> #include <stddef.h> #include <Windows.h> using namespace std; class CTest { private: std::size_t size; char* buffer; public: CTest(std::size_t size) : size(size), buffer(nullptr) { buffer = new c

【Weiss】【第03章】练习3.21:单数组模拟双栈

[练习3.21] 编写仅用一个数组而实现两个栈的例程.除非数组的每一个单元都被使用,否则栈例程不能有溢出声明. Answer: 很简单,一个栈从数组头起,一个栈从数组尾起,分别保留左右栈头索引. 如left=5则表示array[0]~array[4]为左栈元素,right=7则表示array[8]~array[size-1]为右栈元素. 当左右索引交叉时(left=right+1),0~left-1为左栈,left~size-1为右栈,刚好用完每一个单元. 实现代码: 1 //练习3.21新增,

1821 最优集合(单调栈+模拟)

1821 最优集合 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 一个集合S的优美值定义为:最大的x,满足对于任意i∈[1,x],都存在一个S的子集S',使得S'中元素之和为i. 给定n个集合,对于每一次询问,指定一个集合S1和一个集合S2,以及一个数k,要求选择一个S2的子集S3(|S3|<=k),使得S1∪S3的优美值最大. (集合元素可以重复) Input 第一行一个数n,(n<=1000) 接下来n行,每行描述一个集合: 第一个数m,表示集合大小,

Python模拟入栈出栈操作

目标: 1.编写菜单,提示用户操作选项(push,pop,view,quit) 2.规则:定义列表,先入栈,后出栈,后入栈,先出栈 1.模拟入栈.出栈操作 >>> list1 = [] >>> list1.append('a') >>> list1 ['a'] >>> list1.append('b') >>> list1 ['a', 'b'] >>> list1.pop() 'b' >>

使用两个队列模拟一个栈

准备笔试,在看相关知识,看到这个问题,如何使用两个队列模拟一个栈,在参考了相关知识下,实现了代码如下: 1 public class Stack<E>{ 2 Queue<E> q1 = null; //队列q1提供压栈功能, 3 Queue<E> q2 = null; //队列q2提供弹栈功能 4 5 public Stack(){ 6 q1 = new LinkedList<E>(); 7 q2 = new LinkedList<E>(); 8

数据结构和算法之栈和队列一:两个栈模拟一个队列以及两个队列模拟一个栈

今天我们需要学习的是关于数据结构里面经常看到的两种结构,栈和队列.可以说我们是一直都在使用栈,比如说在前面递归所使用的的系统的栈,以及在链表倒序输出时介绍的自定义栈类Stack和使用系统的栈进行递归.那么,在这里我们就讲述一下这两个比较具有特色的或者说关系比较紧密的数据结构之间的互相实现问题. 一:两个栈模拟实现一个队列: 栈的特点是先进后出,然而队列的特点是先进先出. public class Queen(Stack s1,Stack s2){ //实现插入的方法 public void ad

两个栈模拟一个队列和两个队列模拟一个栈

此为网易的一道笔试题.到时候秀逗,不知所云.后来研究之后记录下,以备以后经常翻阅. 栈:先进后出 push和pop 队列:先进先出 offer和poll (1)两个栈模拟一个队列 即将先进后出实现先进先出.比较容易理解,只要所有数据先往一个栈里push,然后将该栈中的数据依次pop出来再push进第二个队列,则顺序自然颠倒过来了,则每次pop是从第二个队列中取数据. import java.util.*; public class StackQueue{ private Stack<Intege