闲话 20.9.20

闲话

吓死我了我还以为joke3579的闲话永久停更了。

不知道为什么 最近这些天一直在唱《non-breath oblige》
但是没有歌词 因此很抓狂 因为原曲很快我根本记不住词
哪位好心人给一下歌词(

话说饭最近似乎停更了
然后转职成了职业画家(

这是一张画!

好看!
话说他歌的pv都是他(以及勤奋的助手们)画的
饭还在发布《若能化作彗星》后拿这首歌的pv做例子讲了一下呢
事职业画家!

有哪位好心人能和我说一下饭的近况啊(
有新歌吗(多半没有吧我猜)
有那更好了
孩子瘾犯了

一道dp题(?)

给一棵 \(n\) 个点的 AVL树,节点权值即为编号。需要把它删到剩下 \(k\) 个点。构造一种方案使得删完后的树还是 AVL 树且字典序最小。输出每个点在该方案下的存留情况。

\(n \le 10^5, 1\le k \le n\)

这题有 \(O(n\log n)\) 的做法但我不会。讲一下 \(O(n \log^2n)\) 的。

首先贪一下,按照先序遍历枚举节点通过dp查看可达性。

具体地,设 \(f_{i,j}\) 为在原树形态的基础上以 \(i\) 为根节点,构成一棵深度为 \(j\) 的 AVL 需要几个节点。容易写出dp方程:

\[f_{i,j} = \min{(f_{ls, i-1} + f_{rs,i-1},f_{ls,i-2}+f_{rs,i-1}, f_{ls,i-1} + f_{rs,i-2}}) + 1 \]

枚举节点,将该节点与其所有祖先标记为必选,标记过程中更新标记对象父节点的dp数组。随后对根节点进行判断。如果在钦定该节点必选的情况下存在一种深度 \(d\) 使得 \(f_{rt,d} \le k\),该节点就可以被选择。
因此有标记 \(i\) 节点为必选的方式:赋 \(f_{i,0}, f_{i,1}\)\(\inf\)

若不可被选择,需要返回未选的情况。

对单个节点的判断是 \(O(\text {树高}^2)\) 的。由于是 AVL 树,因此树高为 \(O(\log n)\)

code
#include <bits/stdc++.h>
#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;
#define int long long
const int N = 5e5 + 10;
int n, k, t1, rt, u, ans, flag[N], f[N][25], g[N][25];

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

struct node {
    int fa, ls, rs, dp, cnt;
    #define ls(p) avl[p].ls
    #define rs(p) avl[p].rs
    #define fa(p) avl[p].fa
    #define dep(p) avl[p].dp
    #define cnt(p) avl[p].cnt
    #define isls(p) (p == ls(fa(p)))
} avl[N];

void upd(int u) {
    f[u][0] = f[u][1] = 1e9;
    for (int i = 2; i <= dep(u); i++) f[u][i] = min( f[ls(u)][i - 1] + min( f[rs(u)][i - 1] , f[rs(u)][i - 2] ) + 1 , f[ls(u)][i - 2] + f[rs(u)][i - 1] + 1 );
}

void dfs(int u) {
    if (ls(u)) dfs(ls(u));
    if (rs(u)) dfs(rs(u));
    dep(u) = max(dep(ls(u)), dep(rs(u))) + 1;
    upd(u);
    f[u][0] = 0, f[u][1] = 1;
}

bool check(int u) {
    if (flag[u]) return 1;
    flag[u] ++;
    for (int i = 0; i <= dep(u); i++) g[u][i] = f[u][i];
    f[u][0] = 1e9;
    while (u != rt) {
        u = fa(u);
        for (int i = 0; i <= dep(u); i++) g[u][i] = f[u][i];
        upd(u); flag[u] ++;
    } int mn = 1e9;
    for (int i = 1; i <= dep(rt); i++) mn = min(mn, f[rt][i]);
    return mn <= k;
}

void roll_back(int u) {
    flag[u] --;
    for (int i = 0; i <= dep(u); i++) f[u][i] = g[u][i];
    while (u != rt) {
        u = fa(u);
        for (int i = 0; i <= dep(u); i++) f[u][i] = g[u][i];
        flag[u] --;
    }
}

signed main() {
    get(n, k);
    rep(i,1,n) {
        get(t1);
        if (t1 == -1) rt = i;
        else {
            if (t1 > i) ls(t1) = i;
            else rs(t1) = i;
        } fa(i) = t1;
    } memset(f, 0x3f, sizeof f);
    rep(i,0,n) f[i][0] = 0;
    dfs(rt);
    rep(i,1,n) {
        if (check(i)) cout << 1;
        else cout << 0, roll_back(i);
    }
    return 0;
}

置换环

定义在集合 \(\mathbb{P}\) 上的置换为一个 \(\mathbb{P}\rightarrow \mathbb{P}\) 的双射 \(f\)。容易发现,任何一个置换 \(f\) 都可以被大小为 \(\mathbb{|P|}\) 的排列 \(p\) 表示。由置换的定义自然导出单位置换 \(e\) 与置换的复合运算。定义 \(f^k\)\(f\) 自复合 \(k\) 次后得到的置换。对于置换 \(f\),若 \(f^k = e\)\(k(k>0)\) 极小,就称 \(k\)\(f\) 的周期。

对于一个 \(\mathbb{P}\) 上的置换 \(f\) 对应的排列 \(p\) ,我们在一个 \(\mathbb{|P|}\) 个点的图上,由 \(i\)\(p_i\) 连边,会得到一个由环构成的有向图,这称作 \(f\) 的置换环图,其中的环称作 \(f\) 的一个置换环。

断言:\(f\) 的周期为所有置换环大小的最小公倍数。

证明:

\(f\) 仅有一个置换环时定理显然成立,因为每个节点都需要环大小长度的遍历后才能回到其本身。

首先考察 \(f\) 仅有两个置换环的情况。设这两个环的大小为 \(a\)\(b\)。当 \(a = b\) 时退化到一个置换环的情况,显然成立。因此考虑 \(a \neq b\)。不妨设 \(a < b\)
我们需要的是同时遍历完两个环的最小长度。现讨论遍历两个环的过程中位置的同步度。不妨设遍历 \(\frac{a}{\text{period}(a,b)}\)\(b\) 环后 \(a\) 环被遍历完。容易发现定义是良的,因为当 \(\text{period}(a,b) = 1\) 时显然成立。

考虑当遍历完一个环后在另一个环上余下的长度 \(b - a\)
\(b - a \mid a\),那 \(\text{period}(a,b) = b - a\)。因此有

\[\text{总周期} = \frac{a}{\text{period}(a,b)}\times b = \frac{a}{b - a} \times b = \frac{a \times b}{b - a} = \frac{a \times b}{\gcd(a,b)} = \text{lcm}(a,b) \]

考虑 \(b - a\nmid a\)。由于现在需要让 \(a\)\(b - a\) 同步,因此我们现在需要解决的子问题表述出了 \(\text{period}(a,b) = \text{period}(a,a-b)\)。根据更相减损术,这表明 \(\text{period}(a,b) = \gcd(a,b)\)。这也表示了 \(b - a \mid a\) 是当前情况的特解。

因此当只存在两个环时有

\[\text{总周期} = \frac{a}{\text{period}(a,b)}\times b = \frac{a \times b}{\gcd(a,b)} = \text{lcm}(a,b) \]

同时这给出了将两个大小任意的环归约成一个等价环的方法。

对于 \(k(k >2)\) 个环的情况,可以先将两个环归约为一个环,这就转化到了 \(k-1\) 个环的情况。根据数学归纳法,该断言成立。\(\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\qquad\quad\square\)

posted @ 2022-09-20 19:36  joke3579  阅读(85)  评论(4编辑  收藏  举报