题解:AT_arc228_b [ARC228B] Minimize Topological Order
题目大意
给定一个排列 \(P\),和一个数组 \(A\)。
对于一棵有根树 \(T\),定义好排列为满足所有父子约束的拓扑序,即父节点必须在子节点之前出现。
所有好排列中字典序最小的记为 \(f(T)\)。
现在要找到所有 \(f(T) = P\) 的有根树 \(T\),并输出代价的最小值。
代价定义为求和,所有点的儿子个数的平方乘以 \(A_i\)。
思路
核心思路: 按照 \(P\) 的顺序考虑每一个点,贪心地让它挂在代价最小的位置下面当儿子。
- 根节点一定是 \(P_1\)。
因为根节点没有父亲,所以它一定是第一个输出,也就是 \(P_1\)。
-
每个节点的父亲位置的范围。
对每一个节点,它能挂的范围是在它前面第一个比它大的数到它的前一个。
若我们现在考虑到的点是 \(P_u\),\(P_u\) 的前面第一个 \(P_v > P_u\) 的下标 \(v\) (\(v < u\)),那么现在 \(u\) 的父亲只能是 \([v, u - 1]\)。
右边界的范围是显然正确的,因为这样就保证了对于好的序列的限制(所选取的父亲一定在这个点之前出现)。关于左边界的范围:因为我们如果把 \(P_u\) 这个点挂到了 \(P_v\) 的子树外,那么我们扫到一个时候,因为 \(P_u < P_v\),会优先去选择 \(P_u\) 的方向,实际上 \(v < u\),我们应该需要先选 \(P_v\),所以为了避免这种情况,\(P_u\),\(P_v\),就一定要有祖孙关系制约或者来说至少不让它抢在 \(P_v\) 前面输出。
反过来,如果现在 \(P_v < P_u\),那么 \(P_u\) 继续挂到 \(P_v\) 的上面的点的儿子并没有影响,因为现在如果 \(P_u\) 和 \(P_v\) 做兄弟,那也会先去选 \(P_v\)。
::::info[准确解释]
若将 \(P_u\) 挂在 \(P_v\) 之前的某个节点下面,那么 \(P_u\) 的父亲会先于 \(P_v\) 输出,这会导致 \(P_u\) 变为可用状态时,\(P_v\) 还没有输出,字典序就会选 \(u\)。我们限制 \(u\) 的选取范围不超过 \(v\),就保证了 \(P_u\) 至少不会抢在 \(P_v\) 前面输出。
::::
- 接下来每一个点就只需要选它能挂到的点,并且找挂的最小代价。
实现
- 对于每一个下标 \(i\),我们找到 \(P_i\) 这个点的父亲范围 \([q_i, i - 1]\),这个用单调栈就行了。
- 对于一个下标 \(i\),如果 \(P_i\) 已经有 \(c_i\) 个儿子,那么再增加一个儿子的代价就是
这个我们顺序扫过去,线段树查询区间内代价最小的点,把当前点送过去当儿子。
单点修改这个点的儿子数量更新它的代价。
同时加入这个当前点的代价,它的儿子数量为 \(0\)。
-
注意:先查询 \(i\),后加入 \(i\),因为 \(i\) 不能作为自己的父亲。
-
总时间复杂度 \(O(N \log N)\)。
代码
::::success[\(\text{code}\)]
#include<bits/stdc++.h>
#define int long long
#define fore(i, a, b) for( int i = (a); i <= (b); ++ i)
#define repe(i, a, b) for( int i = (a); i >= (b); -- i)
using namespace std;
const int N = 2e5 + 10;
int n, h[N], A[N];
int a[N];
int c[N];
int ans;
int q[N];
int st[N], top;
inline int ls(int p) {
return (p << 1);
}
inline int rs(int p) {
return ((p << 1) | 1);
}
struct Segtr{
int t[N << 2], g[N << 2];
void pushup(int p) {
t[p] = min(t[ls(p)], t[rs(p)]);
if(t[ls(p)] < t[rs(p)]) g[p] = g[ls(p)];
else g[p] = g[rs(p)];
}
void modify(int p,int l,int r,int pos,int val) {
if(l == r) {
t[p] = val;
g[p] = pos;
return;
}
int mid = (l + r) >> 1;
if(pos <= mid) modify(ls(p), l, mid, pos, val);
else modify(rs(p), mid + 1, r, pos, val);
pushup(p);
}
pair<int,int> query(int p,int l,int r,int L,int R) {
if(L <= l && r <= R) {
return {t[p], g[p]};
}
int mid = (l + r) >> 1;
pair<int,int> res = {1e18, 0};
if(L <= mid) {
pair<int,int> tmp = query(ls(p), l, mid, L, R);
if(tmp.first< res.first) res = tmp;
}
if(R >= mid + 1) {
pair<int,int> tmp = query(rs(p), mid + 1, r, L, R);
if(tmp.first < res.first) res = tmp;
}
return res;
}
void build(int p,int l,int r) {
if(l == r) {
t[p] = 1e18;
g[p] = 0;
return;
}
int mid = (l + r) >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
pushup(p);
}
}T;
signed main()
{
ios::sync_with_stdio(false);
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
cin >> n;
fore(i, 1, n) cin >> h[i];
fore(i, 1, n) cin >> a[i];
fore(i, 1, n) {
A[i] = a[h[i]];
}
h[0] = 1e18;
st[++ top] = 0;
fore(i, 1, n) {
while(top > 0 && h[i] > h[st[top]]) top --;
q[i] = st[top];
if(q[i] == 0) q[i] = 1;
st[++ top] = i;
}
T.build(1, 1, n);
T.modify(1, 1, n, 1, A[1] * (2 * c[1] + 1));
// cout << "Q :";
// fore(i, 1, n) cout << q[i] << ' ';
// cout << '\n';
fore(i, 2, n) {
pair<int,int> res = T.query(1, 1, n, q[i], i - 1);
// cout << "Res :";
// cout << res.first << ' ' << res.second << '\n';
ans = ans + res.first;
int p = res.second;
c[p] ++;
T.modify(1, 1, n, p, A[p] * (2 * c[p] + 1));
T.modify(1, 1, n, i, A[i] * (2 * c[i] + 1));
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号