首页 > 其他分享 >【题解】sequence

【题解】sequence

时间:2022-10-08 22:12:43浏览次数:63  
标签:sequence int 题解 最大值 个数 leq maxn 序列

题意

定义一个满足下列两个条件之一的序列 \(a\) 为单谷序列:

  1. \(\exists 1 \leq k \leq n\),有 \(a_1 < ... < a_k > a_{k + 1} > ... > a_n\)

  2. \(\exists 1 \leq k \leq n\),有 \(a_1 > ... > a_k < a_{k + 1} < ... < a_n\)

现在给出 \(n\) 次操作,每次操作为往序列末尾加入一个数。对于每次操作,询问使用 swap 操作将序列变成单谷的最小操作次数。

\(n \leq 3 \times 10^5, 1 \leq x \leq 10^9\)

思路

推理。

不妨以转化成第二种单谷序列为例,第一种的情况可以通过将值域对称,然后用第二种的方法求解。两种情况取较小值即为答案。

考虑极端情况。对于最大值,显然它只能转移到序列的两端。将其转移到序列左端的代价为其左边的数的个数,转移到右端的代价同理。假设最大值在下标 \(i\),不妨将把最大值转移到左侧的代价记为 \(f_{i, 0}\),转移到右侧的代价记为 \(f_{i, 1}\),则将最大值归位的代价为 \(\min(f_{i, 0}, f_{i, 1})\)

经过观察,我们发现将最大值归位以后,次大值归位的代价不变。因为最大值已被转移到两端,所以它对其后的转移次数没有贡献。归纳地考虑,记一个数左侧比它小的数的个数为 \(a\),右侧的个数为 \(b\),我们发现将一个数归位的代价为 \(\min(a, b)\)

当序列的长度为定值时,我们只需要通过计算 \(\sum\limits_{i = 1}^n \min(f_{i, 0}, f_{i, 1})\) 即可得出答案。

对于每次操作后暴力计算的复杂度是 \(O(n^2 \log n)\),考虑优化。

简单观察,无论如何操作,\(f_{i, 0}\) 永远为定值,\(f_{i, 1}\) 则单调不降。因此,对于下标 \(i\) 的贡献,在某一个数加入前恒为 \(f_{i, 0}\),在某一个数加入后恒为 \(f_{i, 1}\)。只需要考虑出这个数的位置怎么取,我们就可以快速统计下标 \(i\) 对不同答案的贡献。

容易发现,当 \(f_{i, 0} \geq f_{i, 1}\) 时,将第 \(i\) 个数移动到右侧更优;反之,将第 \(i\) 个数移动到左侧更优。因此,假设有一个下标 \(k\),使得第 \([1, k - 1]\) 个数中有 \(2 f_{i, 0}\) 个小于等于 \(a_i\) 的数,而 \([1, k]\) 中有 \(2 f_{i, 0} + 1\) 个,那么第 \(i\) 个数对于第 \([1, k - 1]\) 次操作的贡献为 \(f_{i, 1}\),对第 \([k, n]\) 次操作的贡献为 \(f_{i, 0}\)

我们可以通过简单的树状数组 + 二分维护下标 \(k\),总复杂度 \(O(n \log^2 n)\)。然后考虑如何统计答案。这里不妨采用 增量法,考虑每一个新加进来的数对于答案的影响。显然地,当第 \(i\) 个数被新加入序列的时候,它会从末尾移动到序列右侧的某一个位置。它的移动次数为序列右侧比它小的数的个数。

我们不妨用一个值域树状数组来维护这些数的个数。对于第 \(i\) 个数,显然它在第 \([1, k - 1]\) 次操作时仍在序列右侧,因此我们需要在第 \(k\) 次询问答案前把它删除。于是我们考虑扫一遍维护答案,总复杂度 \(O(n \log n)\)

综上,总复杂度 \(O(n \log^2 n)\)

代码

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
 
typedef long long ll;
 
const int maxn = 3e5 + 5;
const ll inf = 1e18;
 
int n;
int a[maxn], b[maxn], c[maxn];
int idx[maxn], low[maxn];
ll ans[maxn];
vector<int> del[maxn];
 
int lowbit(int x) { return x & (-x); }
 
void update(int p, int w) { for (int i = p; i <= n; i += lowbit(i)) c[i] += w; }
 
int query(int p)
{
    int res = 0;
    for (int i = p; i; i -= lowbit(i)) res += c[i];
    return res;
}
 
void solve()
{
    memset(c, 0, (n + 1) * sizeof(int));
    for (int i = 1; i <= n; i++)
    {
        low[i] = query(a[i]);
        update(a[i], 1);
        idx[a[i]] = i;
    }
    memset(c, 0, (n + 1) * sizeof(int));
    for (int i = 1; i <= n; i++) del[i].clear();
    for (int i = 1; i <= n; i++)
    {
        int l = idx[i], r = n, ans = l;
        while (l <= r)
        {
            int mid = (l + r) >> 1;
            if (query(mid) <= (low[idx[i]] << 1)) l = mid + 1, ans = mid;
            else r = mid - 1;
        }
        del[ans].push_back(i);
        update(idx[i], 1);
    }
    memset(c, 0, (n + 1) * sizeof(int));
    ll cur = 0;
    for (int i = 1; i <= n; i++)
    {
        cur += query(n) - query(a[i]);
//      printf("debug %d\n", query(n) - query(a[i]));
        for (int v : del[i]) update(v, -1);
        ans[i] = min(ans[i], cur);
        update(a[i], 1);
    }
}
 
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        b[i] = a[i];
        ans[i] = inf;
    }
    sort(b + 1, b + n + 1);
    for (int i = 1; i <= n; i++) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
//  for (int i = 1; i <= n; i++) printf("%d ", a[i]);
//  putchar('\n');
    solve();
    for (int i = 1; i <= n; i++) a[i] = n + 1 - a[i];
    solve();
    for (int i = 1; i <= n; i++) printf("%lld\n", ans[i]);
    return 0;
}

标签:sequence,int,题解,最大值,个数,leq,maxn,序列
From: https://www.cnblogs.com/lingspace/p/xsy-noip2022-39a.html

相关文章

  • 2022洛阳师范学院ACM实验室招新竞赛题解
    A萌新签到题目描述欢迎大家来参加2022洛阳师范学院ACM实验室新生赛,我们实验室全体学长学姐从暑假一直期盼着你们的到来。我们的小萌新那么可爱,学长学姐肯定不会为难大......
  • P4967 黑暗打击 题解
    P4967黑暗打击题解目录P4967黑暗打击题解题目声明分析过程前置知识矩阵加速欧拉函数指数循环节转移矩阵推导利用指数循环节降低时间复杂度代码题目洛谷P4967黑暗......
  • maven打包提示子模块的程序包不存在问题解决
    有些utils和common的模块,已经有依赖,并能正常在Idea上跑了,但打包时提示程序包xxx不存在时,在pom上加上以下代码即可:<build><plugins><plugin><......
  • Subsequence Path(图论,DP)
    题意给定\(N\)个点,\(M\)条边的无向图,边权为\(C_i\)。给定一个序列\(E=(E_1,E_2,\dots,E_K)\),其中\(E_i\)表示边的编号。路径是“好路径”当且仅当边的编号按照经过......
  • TypeError: cli.init is not a function 问题解决
    一、问题对于新手来说,新建一个Reactnative工程时可能会出现如下问题比如在命令行中输入:react-nativeinitchapter2就会报如下错误:这导致工程创建失败,里面仅有node_m......
  • [题解] Codeforces Dytechlab Cup 2022 1737 A B C D E 题解
    傻*Dytechlab还我rating!(不过目前rating还没加上去,据说E是偷的说不定要unrated)实在没预料到会打成这样。。。求点赞点我看题A.ElaSortingBooks从前往后一位一位确......
  • P4392 [BOI2007]Sound 静音问题 题解
    用树状数组维护区间最值,然后求和即可。//P4392[BOI2007]Sound静音问题#include<iostream>#include<cstdio>#include<vector>usingnamespacestd;constintINF......
  • scanf安全问题解决:
    1.加#define_CRT_SECURE_NO_WARNINGS  //必须写在程序的首句2.加#pragmawarning(disable:4996)       //可以写到任意位置 3.将scanf改为scanf_s......
  • LeetCode 阶乘后的零算法题解 All In One
    LeetCode阶乘后的零算法题解AllInOnefactorial阶乘后的零原理图解实现factorial计算后面0的个数,除0!本身的0阶乘!https://www.shuxuele.com/num......
  • PAT程序设计题解
    @目录7-1PY圆面积输入格式:输出格式:输入样例:输出样例:7-2求正弦值输入角度输入格式:输出格式:输入样例:输出样例:7-3PY时间差输入格式:输出格式:输入样例:输出样例:......