首页 > 其他分享 >在相思树下 III 题解

在相思树下 III 题解

时间:2024-08-17 19:15:25浏览次数:13  
标签:lbrace rbrace min int 题解 相思树 max 操作 III

前言

题目链接:洛谷

赛时脑子坨成一坨了,估计是 T1 的影响,写一篇题解来理清思路。

题意简述

给你一个长为 \(n\) 的序列 \(a_{1\dots n}\),你需要对它进行两种操作共 \(n-1\) 次。

对一个长度为 \(l\) 的序列 \(b_{1\dots l}\) 进行一次操作将会把序列变为一个长为 \(l-1\) 的序列 \(c_{1\dots l-1}\):

  • 操作一中,\(\forall i\in[1,l),c_i=\max(b_i,b_{i+1})\);
  • 操作二中,\(\forall i\in[1,l),c_i=\min(b_i,b_{i+1})\)。

给定整数 \(m\),你只能进行至多 \(m\) 次操作一。进行 \(n-1\) 次操作后序列 \(a\) 的长度变为 \(1\)。你可以任意安排操作的顺序,求最终剩余的数 \(a_1\) 的最大值。

题目分析

方法 \(1\):二分 + 差分

很容易想到二分,把序列中 \(\geq mid\) 的统统看成 \(1\),反之是 \(0\),问题变成了最后留下的那一个数是不是 \(1\)。

我们不需要考虑 \(1\) 之间,\(0\) 之间的相互操作,应为无论是 \(\min\) 或 \(\max\),\(\geq mid\) 的永远不会 \(< mid\),反之亦然。所以,我们把序列分成若干极长的 \(\tt 01\) 段,考虑这两个操作对两段交界处的影响。

对于取 \(\max\),会把 \(\tt 01\) 变成 \(\tt 11\);对于取 \(\min\),会把 \(\tt 10\) 变成 \(\tt 00\)。由于我们关心 \(1\),所以,前者是把所有极长的 \(1\) 段左边界向左扩展,后者是对所有极长的 \(1\) 段右边界向左收缩。

考虑扩展和收缩的先后顺序。在答案的操作序列中,对于相邻的两个操作,把先收缩再扩展的,变成先扩展再收缩一定不劣。原因是,先收缩可能会完全消除某一个长度为 \(1\) 的段,但是后者不会。邻项交换证明得,答案就是先扩展,再收缩。显然,多用扩展一定不劣。所以,我们确定了答案的操作序列。

先扩展 \(m\) 次,再收缩 \(n - m - 1\) 次,相当于我们对于初始的极长 \(1\) 段的左端点 \(l\),把 \([l - m, l - 1]\) 覆盖成 \(1\),最后看看 \([1, n - m]\) 是否均为 \(1\)。这些操作直接差分就行了。因为我们只用知道那些地方一次都没被覆盖过。

时间复杂度:\(\Theta(n \log V)\),当然离散化后 \(V = \mathcal{O}(n)\)。

方法 \(2\):单调队列

能否优化呢?我们可以从另一种思路来分析。感觉是贪心,考虑答案操作序列的相邻两个操作,是先取 \(\max\) 还是先取 \(\min\)。不妨列出式子,对于之前连续的三个数 \(a, b, c\),操作两次后得到的 \(a'\) 分别为 \(\max \lbrace \min \lbrace a, b \rbrace, \min \lbrace b, c \rbrace \rbrace\),\(\min \lbrace \max \lbrace a, b \rbrace, \max \lbrace b, c \rbrace \rbrace\)。然后凭借你的数学直觉……额,看不出来……没关系!枚举 \(1 \sim 3\) 的全排列来看看:

\(a, b, c\) 先 \(\min\) 再 \(\max\) 先 \(\max\) 再 \(\min\)
\(1, 2, 3\) \(\max \lbrace \min \lbrace 1, 2 \rbrace, \min \lbrace 2, 3 \rbrace \rbrace = 2\) \(\min \lbrace \max \lbrace 1, 2 \rbrace, \max \lbrace 2, 3 \rbrace \rbrace = 2\)
\(1, 3, 2\) \(\max \lbrace \min \lbrace 1, 3 \rbrace, \min \lbrace 3, 2 \rbrace \rbrace = 2\) \(\min \lbrace \max \lbrace 1, 3 \rbrace, \max \lbrace 3, 2 \rbrace \rbrace = 3\)
\(2, 1, 3\) \(\max \lbrace \min \lbrace 2, 1 \rbrace, \min \lbrace 1, 3 \rbrace \rbrace = 1\) \(\min \lbrace \max \lbrace 2, 1 \rbrace, \max \lbrace 1, 3 \rbrace \rbrace = 2\)
\(2, 3, 1\) \(\max \lbrace \min \lbrace 2, 3 \rbrace, \min \lbrace 3, 1 \rbrace \rbrace = 2\) \(\min \lbrace \max \lbrace 2, 3 \rbrace, \max \lbrace 3, 1 \rbrace \rbrace = 3\)
\(3, 1, 2\) \(\max \lbrace \min \lbrace 3, 1 \rbrace, \min \lbrace 1, 2 \rbrace \rbrace = 1\) \(\min \lbrace \max \lbrace 3, 1 \rbrace, \max \lbrace 1, 2 \rbrace \rbrace = 2\)
\(3, 2, 1\) \(\max \lbrace \min \lbrace 3, 2 \rbrace, \min \lbrace 2, 1 \rbrace \rbrace = 2\) \(\min \lbrace \max \lbrace 3, 2 \rbrace, \max \lbrace 2, 1 \rbrace \rbrace = 2\)

容易发现,由于大的数在取最值得时候没取到,前者是不优的。这也恰好对应了我们方法 \(1\) 中的结论。

所以,依然得出了先弄 \(m\) 次操作一,再弄 \(n - m - 1\) 次操作二。

在前 \(m\) 次操作后,对于一个数 \(a_i\),它最终的值便是 \(\max \lbrace a_{i \dots i + m} \rbrace\),使用 ST 表,线段树都行,或者滑动窗口用单调队列。对于后 \(n - m - 1\) 次操作同理,不过我们只要知道 \(a_1\),所以求一遍 \(a_{1 \dots n - m}\) 的 \(\min\) 即可。

时间复杂度:\(\Theta(n)\)。

代码

方法 \(1\):二分 + 差分

#include <cstdio>
#include <iostream>
using namespace std;

const int N = 1000010;

int n, m, val[N];

int yzh[N];

inline bool check(int x) {
    for (int i = 1; i <= n; ++i) yzh[i] = (val[i] >= x) - (val[i + 1] >= x);
    for (int i = n; i >= 1; --i) {
        if (val[i] >= x && val[i - 1] < x)
            --yzh[max(0, i - m - 1)], ++yzh[i - 1];
        yzh[i] += yzh[i + 1];
    }
    for (int i = 1; i <= n - m; ++i) if (yzh[i] <= 0) return false;
    return true;
}

signed main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &val[i]);
    int l = 1, r = 1e9, ans = 736520;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) ans = mid, l = mid + 1;
        else r = mid - 1;
    }
    printf("%d", ans);
    return 0;
}

方法 \(2\):单调队列

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 1000010;

inline void read(int &x) {
	x = 0; char ch = getchar();
	for (; !isdigit(ch); ch = getchar());
	for (; isdigit(ch); x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar());
}

int n, m, val[N];
int ans = 0x3f3f3f3f;

int Q[N], head, tail;

signed main() {
    read(n), read(m);
    for (register int i = 1; i <= n; ++i) read(val[i]);
    
    head = 0, tail = -1;
    for (register int i = n; i >= 1; --i) {
        while (head <= tail && Q[head] > i + m) ++head;
        while (head <= tail && val[Q[tail]] <= val[i]) --tail;
        Q[++tail] = i;
        if (i <= n - m) ans = min(ans, val[Q[head]]);
    }
    
    printf("%d", ans);
    return 0;
}

后记

遇到贪心操作的题目,考虑答案的操作序列,然后对这个操作序列邻项交换。

二分答案,能够成为答案的和不能成为答案的,看做 \(1\) 和 \(0\),分别考虑。

标签:lbrace,rbrace,min,int,题解,相思树,max,操作,III
From: https://www.cnblogs.com/XuYueming/p/18364514

相关文章

  • n1gr tS0i 题解
    前言题目链接:洛谷。想了一个小时,想到后只用\(1\)分钟过了的题。官方题解过于晦涩,看到一篇很清晰的题解,于是写题解以记之。终于遇到时间瓶颈在输入的题目。题意简述有一个长度为\(n\)的\(\tt01\)串\(S\),你要对\(S\)进行恰好\(n\)次操作。每次操作选择\(1\leq......
  • 洛谷 B3735 B3736 B3787 题解
    嘿嘿我发烧已经好了!题目目录:No.1 B3735[信息与未来2018]圣诞树No.2 B3736[信息与未来2018]最大公约数No.3 B3787[信息与未来2023]精密计时 OK,开始正文!第一题:B3735[信息与未来2018]圣诞树 题目描述圣诞树共有 nn 层,从上向下数第 11 层有 11 个......
  • 题解:AT_arc181_b [ARC181B] Annoying String Problem
    思路首先我们可以根据两个字符串算出另外一个字符串\(T\)的长度。算出来之后,因为我们要满足等式两边完全相等,所以很容易得出这两个字符串应该都是由一些公共的字串拼接而成的。设\(S\)串中最小的周期为\(P\)。所以应该满足\(|P|\Large{\mid}\normalsize\gcd(|S|,|T|)\)......
  • 题解:AT_arc182_a [ARC182A] Chmax Rush!
    思路因为前面不允许出现比这次的替换的值还要大的情况,所以如果我们知道下标\(i,j\)满足\(i<j\)且\(V_i>V_j\)的话,我们就必须把它们两次修改分开。具体地:若\(P_i<P_j\):此时,我们只能将\([1,P_i]\)的值设为\(V_i\),将\([P_j,n]\)的值设为\(V_j\)。若\(P_i>P_j\):此......
  • P8735 蓝跳跳 题解
    Statement给出\(k,p,L\),数序列\(a\),满足如下条件:\(1\lea_i\lek\)\(\sum_ia_i=L\)\(\nexistsi,a_i\gep\landa_{i+1}\gep\)答案对\(20201114\)取模,\(p\lek\le1000,L\le10^{18}\).Solution30pts注意到可以dp,记\(f(i,0/1)\)为凑出\(i\)的方案......
  • 信息学奥赛一本通编程启蒙题解(3011~3015)
    前言Hello大家好,我是文宇.正文3011#include<iostream>usingnamespacestd;intmain(){ inta,b,s; a=880; b=500; s=a*b; cout<<s; return0;}注:没有输入的都可以直接输出.3012#include<iostream>usingnamespacestd;inta,b,t;intmain(){ a=10;b=20......
  • 信息学奥赛一本通编程启蒙题解(3021~3025)
    前言hello大家好,我是文宇。正文3021#include<iostream>usingnamespacestd;inta,b,c,d;intmain(){ cin>>a>>b>>c>>d; cout<<a+b+c+d; return0;}3022#include<bits/stdc++.h>usingnamespacestd;intmain(){ inta,b,c; ......
  • CF704E Iron Man 题解
    Description“铁人”yyb在玩游戏。在一个\(n\)个点的树上,yyb放置了\(m\)个鸡贼。每个鸡贼有四个整数参数\(t_i,c_i,v_i,u_i\),表示这个鸡贼会在\(t_i\)时刻出现在点\(v_i\),并以每时刻\(c_i\)条边的速度向\(u_i\)点匀速移动,到达\(u_i\)点时立刻消失。如果一个时刻......
  • 【题解】「NOIP2012」疫情控制
    https://www.luogu.com.cn/problem/P1084这道题难在贪心的思路,实现比较简单可以直接看代码。首先二分。现在转化为判定问题。可以用倍增求出每个军队最上面能到哪。结论1:我们一定不会把在除了根节点以外的军队往下移动。否则肯定不优。所以我们把能走到根节点的先存在一起......
  • [题解] [HNOI2016] 序列
    原题链接题面给定长度为$n$的序列:$a_1,a_2,\cdots,a_n$,记为\(a[1\colonn]\)。类似地,\(a[l\colonr]\)($1\leql\leqr\leqN$)是指序列:$a_{l},a_{l+1},\cdots,a_{r-1},a_r$。若\(1\leql\leqs\leqt\leqr\leqn\),则称$a[s\colont]$是$a[......