Contest 10C:Ascending Tree

Contest 10C:Ascending Tree

树上保序回归。

[浅谈保序回归问题及其特殊条件下的更优替代解法](浅谈保序回归问题及其特殊条件下的更优替代解法 - Graygoo 的博客 - 洛谷博客 (luogu.com.cn))

保序回归问题,说白了就是给你一张有向图 GG,并给定每个点的两个属性 wiw_i,yiy_i,要求一个序列 fif_i,满足对于图中所有有向边 uvu\to v,均有 fufvf_u≤f_v。额外给定正整数 pp,要求最小化 i=1nwifiyip\sum_{i=1}^{n}w_i |f_i-y_i|^p。我们统称这类问题为 LpL_p 问题。

保序回归问题总共有三种解法:动态规划的维护折线优化,整体二分,网络流。

介绍第二种解法。

考虑赋值 aiai(ndepi+1)a_i \leftarrow a_i-(n-dep_i+1),转化为 \leq 的问题。

我们考虑通过整体二分逐一确定每个 fif_i 的取值。

(以下参考《浅谈保序回归问题————高睿泉》)

首先有:L1L1 问题存在 fif_i 全是整数的最优解。

定义原问题的 S={a,b}(a<b)S=\{a,b\}(a<b) 问题表示在原问题的基础上,强制要求所有 fi[a,b]f_i\in[a,b],最小化上式的值。

那么假设 i,yi(a,b)\forall i,y_i \notin (a,b)fif_i^{'} 为原问题的一组解满足 fi(a,b)f_i^{'}\notin(a,b),那么我们通过将所有 <a<afif_i^{'} 变成 aa>b>bfif_i^{'} 变成 bb,也可以得到原问题的 SS 问题的一组解。

简单证明,考虑 yi(a,b)y_i \notin (a,b)

由上结论可得,我们整体二分出 S={mid,mid+1}S=\{mid,mid+1\},此时显然不存在 yi(mid,mid+1)y_i \in (mid, mid+1),于是我们考虑用一些方法对此求出 SS 问题的一种解 gig_i,那么 gi=midg_i=midgi=mid+1g_i=mid+1

假设 gig_i 对应原问题的解 fif_i

那么若 gi=midg_i=mid,则原问题中 fi[l,mid]f_i \in [l, mid];否则 gi=mid+1g_i=mid+1,则 fi[mid+1,r]f_i\in[mid + 1,r]

于是根据 gig_i 的取值,将 ii 分到左半段和右半段,继续二分即可。

对于本题,使用树形 dp 求出 gig_i 即可,具体地,设 dpi,0/1dp_{i,0/1} 表示点 iimid/mid+1mid/mid + 1 时子树的最小值,由此可求出最优方案。

时间复杂度单 log

以下是一些优化:

  • 观察到 1pi<i1\leq p_i<i,于是将一大堆 dfs 弄成拓扑序遍历。
  • 观察到转移成 \leq 的问题后,答案的解 fi{ai}f_i \in \{a_i\},于是 fif_i 的取值只有 O(n)\mathcal O(n) 种,我们整体二分的取值也只有 O(n)\mathcal O(n) 种,于是复杂度优化为 O(nlogn)\mathcal O(n\log n)
  • 本质上整体二分可以队列优化的,但效果不可观。

推 [基环树上保序回归] loj 2470. 「2018 集训队互测 Day 2」有向图

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define re register
typedef long long ll;
namespace Fread {const int SIZE = 1 << 20; char buf[SIZE], *S, *T; inline char getchar() {if (S == T) {T = (S = buf) + fread(buf, 1, SIZE, stdin); if (S == T) return '\n';} return *S++;}}
#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#endif
inline int ri() {
    re int x = 0;
    re bool t = 0;
    re char c = getchar();
    while (c < '0' || c > '9') t |= c == '-', c = getchar();
    while (c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
    return t ? -x : x;
}
const int _ = 5e5 + 5;
int n, a[_], b[_], p[_], p1[_], p2[_], col[_], inq[_], inde, fa[_];
ll f[_][2], ans;
inline void solve(re int l, re int r, re int L, re int R) {
    re int i;
    if(l > r) return;
    if(L == R) {
        for(i = l; i <= r; ++i) ans += abs(a[p[i]] - b[L]);
        return;
    }
    re int mid = (L + R) >> 1;
    ++inde;
    for(i = l; i <= r; ++i) inq[p[i]] = inde, f[p[i]][0] = abs(a[p[i]] - b[mid]), f[p[i]][1] = abs(a[p[i]] - b[mid + 1]);
    for(i = r; i >= l; --i) (inq[fa[p[i]]] == inde) ? (f[fa[p[i]]][0] = (f[fa[p[i]]][0] + f[p[i]][0]), f[fa[p[i]]][1] = (f[fa[p[i]]][1] + min(f[p[i]][0], f[p[i]][1]))) : 0;
    re int t1 = 0, t2 = 0;
    for(i = l; i <= r; ++i) col[p[i]] = (inq[fa[p[i]]] == inde && !col[fa[p[i]]]) ? (p1[++t1] = p[i], 0) : (f[p[i]][0] < f[p[i]][1] ? (p1[++t1] = p[i], 0) : (p2[++t2] = p[i], 1));
    for(i = 1; i <= t1; ++i) p[l + i - 1] = p1[i];
    for(i = 1; i <= t2; ++i) p[l + t1 + i - 1] = p2[i];
    solve(l, l + t1 - 1, L, mid), solve(l + t1, r, mid + 1, R);
}
signed main() {
    n = ri(), a[1] = ri();
    re int i;
    for(i = 2; i <= n; ++i) fa[i] = ri(), a[i] = ri();
    for(i = 1; i <= n; ++i) {
        p1[i] = p1[fa[i]] + 1, b[i] = a[i] = a[i] - (n - p1[i] + 1);
        p[i] = i;
    }
    sort(b + 1, b + n + 1);
    int tt = unique(b + 1, b + n + 1) - b - 1;
    solve(1, n, 1, tt);
    printf("%lld\n", ans);
}
posted @ 2023-07-14 20:12  蒟蒻orz  阅读(8)  评论(0编辑  收藏  举报  来源