闲话 22.8.15

闲话

今天的歌是《Seventina》!
是饭自造的一个词,如果翻译的话大概是“十七年华”之类的

Seventina

間違ってばっかの私は
总是犯下各种错误的我
「大丈夫なんだ」って言うけれど
虽然嘴上总说着「没关系,没关系」
ほんとの本当はガラスよりも繊細だ
可是其实内心要比玻璃更加纤细
強がってばっかの私は
总是什么事都逞强的我
そういえば学校の帰り道
说起来在放学回家的路上
なんども何度も泣いたこともあったっけ
自己偷偷掉过的眼泪好像也数不清了吧

夢と現実と画面の間
梦和现实和画面之间
月に何回か限りのミッドナイト
每月不知能有几回的夜晚
セブンティーンなフォーチュネス
17岁般的幸福
だけどたまに不安になって
但偶尔还是会变得不安起来
わかんないよフューチャー
对于未来,可是一无所知啊
10年後の私なんて
10年后的我什么的
セブンティーンはセンシティブ
17岁纤细敏感
ほんの小さなくしゃみで
哪怕只打了个小小的喷嚏
世界ひとつ終わるくらい
就好像终结了个世界一样
なんて脆弱なんだろうなセブンティーナ
你说这Seventina,她是得有多么脆弱啊

で、学校行ったら俯き気味で
然后在学校以低着头的姿态
自分の席までシミュレイション
这么预想着到达自己座位的路线
気まずいあの子と仲良いあの子の
关系不太好和关系不错的女生们的
対角線上点Me
对角线上是Me

ひとりぼっちにはなんない様に
让自己看起来不会那么形单影只
だいぶ繊細なガラスのハートさ
这颗玻璃心啊可是有够纤细的了
セブンティーンはロンリネス
17岁寂寞不已
だからいつも不安になって
所以啊还是会变得不安起来
わかっちゃうよフューチャー
对于未来,我不小心知道了呢
1000年後の私なんて
1000年后的我什么的
セブンティーンはカンガエル
17岁在思考着
私が泣いた数だけ
就好比我每哭一次
知らぬ人が笑ふくらひ
就会有谁笑了一次那样
あな不平等なんだろうなセブンティーナ
你说这Seventina她是有多不公平啊

大人になること
变成大人这件事
水を飲むこと
将水饮下这件事
私達が永遠じゃないこと
我们不能成为永恒这件事
当たり前でも
即便理所当然
そんなことでも
即便是那样的事
全部キラキラしていたんだ
全部都曾经闪闪发亮过呢
それが18,19,20
然后就18,19,20
だんだん見えなくなっていく
慢慢地开始变得什么也看不见
それが21,22…
接下来就21,22...
私は覚えていられるかな
17岁的时光还会记得住吗

セブンティーンじゃ無いなら
如果有一天不再是17岁的话
もしも無くしちゃったようなら
如果有一天忘掉了那些回忆的话
何度だって言うよ
无论几次都会说喔
この世界の愛し方を
说怎样去爱这个世界
セブンティーンはエンドレス
17岁永恒不息
史上最弱で最強の
史上最弱但是最强的
私のこと思い出せば
若能想起那时候的我
もう怖いもんなんて無いな
就再也不会感到害怕了吧
セブンティーナ
Seventina

間違ってばっかの私は
总是犯各种错误的我啊
間違ってばっかの私は
总是犯各种错误的我啊

饭这首歌确实把将成年而未及的少年少女心态体现得淋漓尽致
谁还没有少年过呢
听着这首歌心里总是感触良多
歌里谈到的想法我也不是没有过

セブンティーンじゃ無いなら
如果有一天不再是17岁的话
もしも無くしちゃったようなら
如果有一天忘掉了那些回忆的话
何度だって言うよ
无论几次都会说喔
この世界の愛し方を
说怎样去爱这个世界

一道线段树优化dp

P8476 「GLR-R3」惊蛰

给定非负整数序列 \(\{a_n\}\),定义函数 \(f(x,y)\)

\[f(x,y)=\begin{cases} x-y,&x\ge y\\ C,&x< y \end{cases}, \]

其中 \(C\) 是给定常数。请构造一个不增非负整数序列 \(\{b_n\}\),最小化

\[\sum_{i=1}^nf(b_i,a_i). \]

你仅需输出这一最小化的结果。

\(n \le 1e6, V = \max(a[i]) \le 1e9\)

一眼dp,考虑状态设置方法。

1. 我会暴力dp!

期望得分45,实际得分45。

定义 \(f[i][j]\) 为填完前 \(i\) 个位置,\(b[i] = j\) 的最小花费。显然有转移方程

\[f[i][j] = (\min_{k = j}^n f[i-1][k])\ + [j < a[i]] \times C + [j \ge a[i]] \times (j - b[i]) \]

后缀 \(\min\) 优化即可。复杂度 \(O(nV)\)

2. 我发现 \(b[i]\) 的取值可以不连续!

期望得分60,实际得分60。

进行一波散化的离,记不同的数字有 \(m\) 个,其中从小到大第 \(k\) 个记为 \(\text{dsc}[k]\)。随后改变dp数组的定义方式,定义 \(f[i][j]\) 为填完前 \(i\) 个位置,\(b[i] = dsc[j]\) 的最小花费。显然有转移方程

\[f[i][j] = (\min_{k = j}^m f[i-1][k])\ + [dsc[j] < a[i]] \times C + [dsc[j] \ge a[i]] \times (dsc[j] - b[i]) \]

由于 \(m = O(n)\),复杂度为 \(O(n^2)\)

3. 我想要直接在一个维度的本数组上转移!

期望得分100,实际得分100。

我们发现,若把一个位置和后缀取min后实际上转移可以在 \(f[]\) 数组上本位处进行。

考虑第 \(i\) 次转移对答案产生的贡献。
\(a[i]\) 为第 \(k\) 大的元素,则

  1. \(j \in [k+1, n], f[i][j] = f[i-1][j] + C\)
  2. \(j \in [1,k-1], f[i][j] = f[i-1][j] + dsc[j] - a[i]\)

考虑使用线段树优化dp。第一个操作与第二个操作 \(-a[i]\) 部分可以直接区间加,不展开。
第二个操作 \(+dsc[j]\) 不好处理。但我们发现一件事,既然 \(f\) 数组取了后缀 \(\min\),那他是不降的,而 \(\text{dsc}\) 数组显然不降。因此,假如 \(f\) 数组原来的最小值在最左侧,加 \(\text{dsc}\) 后最小值还在最左侧。
再考虑后缀最小值如何做。
我们发现以 \(k\) 为分界线前后分别是两个不降序列,二者首尾相连。既然取的是前缀 \(\min\),直接拿首位元素在后面序列上二分覆盖最小值即可。
时间复杂度 \(O(n \log n)\)

题解 by myself
#include <bits/stdc++.h>
#define N 2000005
#define int long long
#define rep(i,a,b) for ( register int (i) = (a); (i) <= (b); ++(i) )
#define pre(i,a,b) for ( register int (i) = (a); (i) >= (b); --(i) )
using namespace std;
int n, m, c, a[N], b[N], id[N], cnt;

char buf[1<<21], *p1 = buf, *p2 = buf;
inline char gc() { return (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++); }
#ifndef ONLINE_JUDGE
	#define gc getchar
#endif
template <typename T>
inline void get(T & x){
	x = 0; char ch = gc(); bool f = false;
	while (ch != '-' and (ch < '0' or ch > '9')) ch = gc();
    if (ch == '-') f = true, ch = gc();
    while (ch >= '0' and ch <= '9') x = (x<<1) + (x<<3) + (ch^48), ch = gc();
	if (f) x = -x;
} 

struct SegmentBeats {
    int val, flag, lazy1, lazy2;
    #define ls (p<<1)
    #define rs (p<<1|1)
    #define mid (l + r >> 1)
    #define val(p) (seg[p].val)
    #define flag(p) (seg[p].flag)
    #define lzy1(p) (seg[p].lazy1)
    #define lzy2(p) (seg[p].lazy2)
} seg[N << 2];

void ps_f(int p) {
    if(flag(p) == 0) return;
    val(ls) = val(rs) = val(p);
    lzy1(ls) = lzy1(rs) = 0;
    lzy2(ls) = lzy2(rs) = 0;
    flag(ls) = flag(rs) = 1;
    flag(p) = 0;
}

void ps_d(int md, int r, int p) {
    ps_f(p); 
    ps_f(ls), ps_f(rs);
    if(lzy1(p)) {
        val(ls) += lzy1(p);  val(rs) += lzy1(p);
        lzy1(ls) += lzy1(p);  lzy1(rs) += lzy1(p);
        lzy1(p) = 0;
    } if(lzy2(p)) {
        val(ls) += b[md] * lzy2(p);  lzy2(ls) += lzy2(p); 
        val(rs) += b[r] * lzy2(p); lzy2(rs) += lzy2(p);
        lzy2(p) = 0;
    }
}

void ps_p(int p) { val(p) = min(val(ls), val(rs)); }

int res;
void upd1(int p, int l, int r, int pos, int v1, int v2) {
    if(l != r) ps_d(mid, r, p);
    if(r <= pos) {
        ps_f(p);
        val(p) += v1, lzy1(p) += v1;
        val(p) += b[r], lzy2(p)++;
        if(r == pos) res = val(p);
        return;
    } if (l > pos) {
        ps_f(p);
        val(p) += v2, lzy1(p) += v2;
        return;
    } upd1(ls, l, mid, pos, v1, v2);
    upd1(rs, mid+1, r, pos, v1, v2);
    ps_p(p);
}

bool upd2(int p, int l, int r, int pos, int val) {
    if (l != r) ps_d(mid, r, p);
    if(l > pos) {
        if(val(p) > val) {
            val(p) = val, lzy1(p) = lzy2(p) = 0;
            flag(p) = 1;
            return 1;
        } if (l != r) {
            if (upd2(ls, l, mid, pos, val)) 
                upd2(rs, mid+1, r, pos, val);
        } return 0;
    } if (l == r) return 0;
    if(pos < mid) {
        if (upd2(ls, l, mid, pos, val)) 
            return upd2(rs, mid+1, r, pos, val);
        return 0;
    } return upd2(rs, mid+1, r, pos, val);
}

signed main() {
	get(n), get(c);
    rep(i,1,n) get(a[i]), b[i] = a[i];
    sort(b+1, b+1+n);
    m = unique(b+1, b+1+n) - b - 1;
    rep(i,1,n) id[i] = lower_bound(b+1, b+1+m, a[i]) - b, id[i] = m - id[i] + 1;
    reverse(b+1, b+1+m);
    rep(i,1,n) {
        upd1(1, 1, m, id[i], -a[i], c);
        upd2(1, 1, m, id[i], res);
    } 
    printf("%lld", val(1));
    return 0;
}

诶嘿又是一道

\(n\) 对数对 \((a_i,b_i)\),第 \(i\) 对的权值为 \(w_i\)。你需要从中选择一部分数对并以任意顺序排序,满足 \(\forall i < j,a_i \le b_j\),同时最大化选择的权值。

\(n \le 10^5, a_i, b_i, c_i \le 10^9\)

首先我们需要排个序,使得只能由前面的数对对后面贡献。
设前面的数对为 \(i\),后面的数对为 \(j\)。由题意有 \(a_i \le b_j\)。同时,由于 \(j\) 无法对 \(i\) 作贡献,有 \(a_j > b_i\)。由不等式基础知识得到 \(a_i + b_i < a_j + b_j\) 。但这有一点问题。假设 \((a_i,b_i) = (6,2),(a_j,b_j) = (5,3)\),这显然是不行的,\((5,3)\) 放在前面显然对其他数对的贡献更大。因此我们按 \(a+b\) 为第一维,\(a\) 为第二维排序即可。

然后考虑如何转移。
我们先离散化一下,把值域扔到 \(O(n)\) 内部。
根据第一题,我们可以设 \(f[i][j]\) 表示选到第 \(i\) 个,目前已选数对中 \(a\) 的最大值为 \(j\) 的最大权值。
然后我们可以发现如果把第一维压掉又变成了本位转移。讨论第 \(i\) 个数对的贡献。
对这个数对本身,我们有这样的更新方式:

\[f[i][a_i] = \max_{k = 1}^{min(a_i, b_i)} f[i-1][k] + w_i \]

关于 \(\max\) 上面的那个东西,它表示只有 \(\max_{k = 1}^{i-1}a_k \le b_i\) 的位置才能被选择,只有 \(\max_{k = 1}^{i-1}a_k \le a_i\) 的位置才能用来更新。

对这个数对能贡献的所有位置,我们有这样的转移方程:

\[\forall a_i < k \le b_i, f[i][k] += w_{i} \]

关于 \(\forall\) 后面的那个东西,它表示只有 \(a\) 的最大值大于 \(a\) 的位置才能无影响地加入目前的值,只有 \(a_{\max} \le b_i\) 的位置才满足条件。

然后我们发现一件事
我们只需要区间取max、单点改max、区间加
扔到线段树上就完了。
时间复杂度 \(O(n \log n)\)

代码
vector<int> lsh;
struct pairs{
    int a, b, w;
    bool operator < (const pairs & s) const { if (a + b != s.a + s.b) return a + b < s.a + s.b; return a < s.a; }
} p[N];

struct SegmentBeats {
    int mx, lazy;
    #define ls (p<<1)
    #define rs (p<<1|1)
    #define mid (l + r >> 1)
    #define mx(p) (seg[p].mx)
    #define lazy(p) (seg[p].lazy)
} seg[N << 2];

void ps_p(int p) { mx(p) = max(mx(ls), mx(rs)); }
void ps_d(int p) { if(lazy(p) == 0) return; mx(ls) += lazy(p), mx(rs) += lazy(p), lazy(ls) += lazy(p), lazy(rs) += lazy(p); lazy(p) = 0;}

void upd1(int p, int l, int r, int L, int R, int val) {
    if(L <= l and r <= R) {
        mx(p) += val, lazy(p) += val;
        return;
    } ps_d(p);
    if (L <= mid) upd1(ls, l, mid, L, R, val);
    if (mid < R) upd1(rs, mid+1, r, L, R, val);
    ps_p(p);
}

void upd2(int p, int l, int r, int pos, int val) {
    if (l == r) { mx(p) = max(mx(p), val); return; }
    ps_d(p);
    if (pos <= mid) upd2(ls, l, mid, pos, val);
    else upd2(rs, mid+1, r, pos, val);
    ps_p(p);
}

int qry(int p, int l, int r, int L, int R) {
    if (R < l or r < L) return -1;
    if (L <= l and r <= R) return mx(p);
    ps_d(p);
    return max(qry(ls, l, mid, L, R), qry(rs, mid+1, r, L, R));
}

rep(i, 1, n) get(p[i].a), get(p[i].b), get(p[i].w), lsh.emplace_back(p[i].a), lsh.emplace_back(p[i].b);
sort(p + 1, p + 1 + n);
sort(lsh.begin(), lsh.end());
lsh.erase(unique(lsh.begin(), lsh.end()), lsh.end());
rep(i, 1, n) {
    p[i].a = lower_bound(lsh.begin(), lsh.end(), p[i].a) - lsh.begin() + 1;
    p[i].b = lower_bound(lsh.begin(), lsh.end(), p[i].b) - lsh.begin() + 1;
} m = lsh.size();
rep(i, 1, n) {
    int mx = qry(1, 1, m, 1, min(p[i].a, p[i].b));
    upd2(1, 1, m, p[i].a, mx + p[i].w);
    if(p[i].a < p[i].b) upd1(1, 1, m, p[i].a+1, p[i].b, p[i].w);
} printf("%lld", qry(1, 1, m, 1, m));
posted @ 2022-08-15 17:56  joke3579  阅读(87)  评论(4编辑  收藏  举报