[题解]高级监狱Ⅱ

\[\color{red}{\text{校长者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{The principal is really a god}} \\ \color{pink}{\text{with a closestool on the left and Yongshen on the right}} \\ \color{pink}{\text{holding a sharp pen to pierce the truth}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{校長は本当に神であり、左側にトイレ、右側にヨンシェンがあり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Le principal est vraiment un dieu}} \\ \color{lightblue}{\text{avec des toilettes à gauche et Yongshen à droite}} \\ \color{lightblue}{\text{tenant un stylo pointu pour percer la vérité}} \\ \color{lightblue}{\text{Qui peut lui résister ? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Principalis deus est, Yongshen a dextris cum latrina}} \\ \color{cyan}{\text{acuto stylo ad perforandum veritatem: quis resistet ei? }} \\ \hline \end{array} \\ \color{red}{\text{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\brak[1]{\left({#1}\right)} \newcommand\Brak[1]{\left\{{#1}\right\}} \newcommand\d[0]{\text{d}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \newcommand\rhs[1]{\;\text{Rhs}\;#1} \newcommand\lhs[1]{\;\text{Lhs}\;#1} \newcommand\Vec[1]{\vec{\mathbf{#1}}} \newcommand\rank[0]{\text{rank}} \]




  \(\mathcal{Back\;To\;The\;Menu}\).

2022-02-08 高级监狱 Ⅱ

数列 / Array

  莫名其妙发现了正解......考虑不降怎么做?事实上可以直接写转移方程:

\[f(i)=\max_{j=1}^i f(j)+(a_i-a_j)^2+C \\ \Rightarrow f(i)=a_i^2+C+\max_{j=1}^i f(j)+a_j^2-2a_ja_i \]

  如果我们将 \(a_i\) 看作 \(x\),那么我们想求

\[\max_{j=1}^i -2a_jx+f(j)+a_j^2 \]

  可以用李超树维护直线 \(y=-2a_j+a_j^2+f(j)\),最后枚举最后一个划分点在哪里,取 \(f(i)\) 最大值即可,复杂度 \(\mathcal O(n\log n)\).

  接下来说正解,实际上直接把不降的代码拿去跑就可以过这道题......也就是说,我们直接将每个点视作它所在那一段的最大值直接转移即可,为什么这样是对的呢?假设最优情况是从 \(j\) 转移到 \(i\),不妨令 \(a_j<a_i\)(如果 \(a_j>a_i\),讨论是一样的),我们讨论几种情况:

  1. 存在 \(j<k<i\),且 \(a_j<a_i<a_k\),那么从 \(j\) 转移到 \(i\) 不如 \(j\to k\to i\) 优,与假设矛盾;
  2. 存在 \(j<k<i\),且 \(a_k<a_j<a_i\),那么从 \(j\) 转移到 \(i\) 不如 \(j\to k\to i\) 优,与假设矛盾;
  3. 在前两者都不存在的情况下,只有可能是 \(\forall k\in (j,i),a_j<a_k<a_i\),我们显然可以将划分点设在 \(j\),这样 \(j\) 就是前面那一段最大的了;

  于是直接做就好了,复杂度 \(\mathcal O(n\log n)\).

/** @author Arextre */

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

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
/** @warning no forced type conversion */
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

typedef long long ll;
typedef unsigned long long ull;
typedef std::pair<int, int> pii;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void chkmin(T& x, const T rhs) { x = std::min(x, rhs); }
template<class T> inline void chkmax(T& x, const T rhs) { x = std::max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}

} // namespace Elaina
using namespace Elaina;

const int maxn = 1e6;
const ll inf = 1ll << 60;

int n, c;
ll a[maxn + 5];

inline void input() {
    readin(n, c);
    rep (i, 1, n) readin(a[i]);
}

/** @warning the upper limit should be the maximum of @p {ai} */
namespace saya {

struct node {
    ll k, b;
    inline node() { k = 0, b = -inf; }
    inline node(ll K, ll B): k(K), b(B) { /** nothing left */ }
    inline ll operator ()(int x) { return k * x + b; }
} tre[maxn << 2 | 2];

#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid (l + r >> 1)
#define _lhs ls, l, mid
#define _rhs rs, mid + 1, r

void insert(node f, int i = 1, int l = 1, int r = maxn) {
    // totally superior
    if (tre[i](l) <= f(l) && tre[i](r) <= f(r)) { tre[i] = f; return ; }
    if (tre[i](mid) < f(mid)) swap(tre[i], f);
    if (tre[i](l) < f(l)) insert(f, _lhs);
    else if (tre[i](r) < f(r)) insert(f, _rhs);
}

ll query(int x, int i = 1, int l = 1, int r = maxn) {
    ll ret = tre[i](x);
    if (l == r) return ret;
    if (x <= mid) return max(ret, query(x, _lhs));
    else return max(ret, query(x, _rhs));
}

#undef ls
#undef rs
#undef mid
#undef _lhs
#undef _rhs

} // namespace saya

ll dp[maxn + 5];

signed main() {
    freopen("array.in", "r", stdin);
    freopen("array.out", "w", stdout);
    input();
    rep (i, 1, n) {
        dp[i] = saya::query(a[i]) + rqr(a[i]) + c;
        if (dp[i] < 0) dp[i] = 0;
        saya::insert(saya::node(-(a[i] << 1), dp[i] + rqr(a[i])));
    }
    ll ans = 0;
    rep (i, 1, n) chkmax(ans, dp[i]);
    writln(ans);
    return 0;
}

差量 / Difference

  着实妙,只能说在交互、二分方面的积累太少了。然而也做过很多交互题,还是应当对交互有一个感觉的。一般的交互都是几个关键:有没有关键的地方?有什么是一个元素自己的特点?

  就该题来说,独特点在于 每个元素互不相同,然而目前我们还不知道关键的地方,不妨先想一想怎么骗一点分。

  就 \(n\le m_1\),是平凡的;当 \(n\le m_1+2m_2\) 时,我们也会做,先尽可能用 (1) 问出足够多的数,假设我们问出了 \(a_0,\cdots,a_{m_1-1}\),考虑如何使用两次 (2) 问出位置 \(p\) 的数 —— 很简单,用 (2) 询问 \(d_0=\text{qry}_2(a_0,a_p)\),再用 (2) 询问 \(d_1=(a_1,a_p)\),那么,在 \(a_0\pm d_0,a_1\pm d_1\) 这四个数中,两个相同的元素就是 \(a_p\).

  当 \(n\le m_1+m_2\) 呢?此时要求使用一次 (2) 就可以确定元素,我们可以尝试用 (2) 询问 \(\text{qry}_2(a_0,a_1,a_p)\),此时我们固然可以得到 \(x=|a_0-a_p|,y=|a_1-a_p|\),但是现在有两个问题 —— 其一是我们不知道哪个是 \(x\),哪个是 \(y\),其二是,即使我们确定了 \(x,y\),但是不知道 \(a_p\)\(a_0,a_1\) 之间的大小关系,此时 \(a_p\) 存在多解,除非一种情况出现:\(|a_1-a_0|=x+y\),这时 \(a_p\) 只有一个解。

  不过不是所有元素都满足 \(\exists i,j\;\text{s.t.}\;a_i<x<a_j\),有两个特别的元素 —— 最大值和最小值。

  此时我们通过骗分遇到的困难发现了 关键点 —— 最大值和最小值有点特别?将目光引到它俩上来之后,我们发现 特点 也可以在它俩上体现 —— 将所有元素与它俩中任意一个作差,每个元素的差一定是唯一的。这再次给予我们提示,关键点 可以体现 特点,那么突破口应当在关键点上。

  既然如此,得想办法把关键点找出来啊?怎么找?这就得自己想一想了:全局询问一次,获得极差,然后对前缀做二分询问,如果该前缀的最大差值等于极差,则说明该前缀包含最大最小值。这样二分之后可以找到一个 端点,这个 端点 对应的数就是最大、最小值中的一个,至于是哪个,我们不需要立刻知道,因为知道它是最大或最小已经可以让我们利用 特点 了。

  接下来就是关键了 —— 我们怎么得到每个元素分别与 端点 的差?考虑每次询问 \(S_i\) 集合的元素,它包含所有二进制下第 \(i\) 位为 \(1\) 的下标,再询问 \(S_i\cup\set{\textsf{端点}}\) 的差,就可以得到 \(S_i\) 中每个元素与 端点 的差,但是我们不知道差的下标啊?注意到差都是不一样的,所以我们将每个差出现的那些集合对应的二进制位拼起来,就可以还原出差的下标。后面的过程就非常朴素了。

  注意将下标二进制分组也是一种很好用的 \(\log\) 式询问法。最后我们会用到 \(\mathcal O(3\log n)\) 次 (2) 询问,绰绰有余了。

#include "difference.h"
#include <bits/stdc++.h>

namespace Work_space {

const int maxn = 250;

vector<int> S, ret, ans, recei;
map<int, int> buc;
// composition: <delta, bit>
map<int, vector<int>> pos;
int delta[maxn + 5];

inline void find(int n, int M1, int M2) {
    for (int i = 0; i < n; ++i) S.push_back(i);
    recei = qry2(S);
    sort(recei.begin(), recei.end());
    int polar = recei.back();
    int p = -1;
    for (int l = 1, r = n - 1, mid; (mid = l + r >> 1, true) && l <= r; ) {
        S.clear();
        for (int i = 0; i <= mid; ++i) S.push_back(i);
        recei = qry2(S);
        sort(recei.begin(), recei.end());
        if (recei.back() == polar) p = mid, r = mid - 1;
        else l = mid + 1;
    }
    for (int b = 0; 1 << b <= n; ++b) {
        S.clear(), buc.clear();
        // add the index by 1
        for (int i = 0; i < n; ++i)
            if (i != p && (i + 1 >> b & 1)) S.push_back(i);
        recei = qry2(S);
        for (const int& v: recei) ++buc[v];
        S.push_back(p); recei = qry2(S);
        for (const int& v: recei) {
            if (buc[v] > 0) --buc[v];
            else pos[v].push_back(b);
        }
    }
    // printf("p == %d\n", p);
    assert(pos.size() == n - 1);
    int mxdel = p;
    for (const auto& [x, vec]: pos) {
        int coord = 0;
        for (const int& b: vec) coord += 1 << b;
        --coord; // pay attention!!!
        delta[coord] = x;
        if (x > delta[mxdel]) mxdel = coord;
    }
    ans.resize(n);
    ans[p] = qry1(p);
    ans[mxdel] = qry1(mxdel);
    int t = 1;
    if (ans[p] > ans[mxdel]) t = -1;
    for (int i = 0; i < n; ++i) if (i ^ p)
        ans[i] = ans[p] + t * delta[i];
    answer(ans);
    return ;
}

} // namespace Work_space

void find(int n, int M1, int M2) { Work_space::find(n, M1, M2); }

异或 / Randomxor

  留坑待补......

posted @ 2022-03-14 21:51  Arextre  阅读(150)  评论(0编辑  收藏  举报