Description

给定一颗 \(n\) 个结点的树,每个点有一个点权 \(v\)。点权只可能为 \(0\) 或 \(1\)。

现有一个空数列,每次可以向数列尾部添加一个点 \(i\) 的点权 \(v_i\),但必须保证此时 \(i\) 没有父结点。添加后将 \(i\) 删除。

这样可以一个长为 \(n\) 的数列 \(x\)。求 \(x\) 中逆序对数的最小值。

Hint

  • \(1\le n\le 2\times 10^5\)
  • \(v_i \in \{0, 1\}\)

Solution

由于一个结点的父结点尚未被删除,那么现在该结点则无法被加入数列。可见题目要求我们 从树根自顶向下 删除。

但显然我们不会这样做——我们 将所有结点视作独立,向父亲方向合并


我们不妨先考虑这样一个问题:对于一个根结点为 \(x\) 的树,其子结点为 \(y_1, y_2, \cdots y_k\)。假设子树 \(y_1, y_2, \cdots y_k\) 都已经合并好了,那么我们只要将这些子树合并答案,向上传答案即可。

首先,由题意得,结点 \(x\) 的点权必须排在最前面。接下来就需要合理安排顺序,使得 跨越子树的逆序对 数量最小。由于子树内在前期早已统计完毕,此处无需再做讨论。

为方便讨论,在这里我们还需维护子树中 \(0, 1\) 的个数,分别记为 \(\text{cnt}(\cdots, 0), \text{cnt}(\cdots, 1)\)。

若要使逆序对尽可能小,而权值就只有 \(0, 1\),第一直觉就是 贪心地把 \(0\) 尽量排前面

但直觉是很模糊的,我们需要一个明确的标准。

对于两个子树 \(y_i, y_j\),如果 \(y_i\) 排在前面,那么会产生 \(\text{cnt}(y_i, 1)\times \text{cnt}(y_j, 0)\) 个逆序对,反正则会产生 \(\text{cnt}(y_j, 1)\times \text{cnt}(y_i, 0)\) 个。

显然我们应选择结果较少的策略——优先选取 \(\dfrac{\text{cnt}(y, 0)}{\text{cnt}(y, 1)}\) 较小的。为避免除以零造成 RE,需要化除为乘。


但此题不能直接递归处理,需要全局一起算,即上文中“将所有结点视作独立,向父亲方向合并”的思路。

那么子树的 \(\text{cnt}\) 值就变成了 连通块 的 \(\text{cnt}\) 值,容易发现上面的贪心思路于此仍然有效。

此处涉及连通块整块信息的维护,不难想到 并查集。连通块的有序维护,可以使用

在每个点向上合并后,父亲方向结点需要删去,这对于堆来说就不太方便(当然可以考虑 multiset 或 可删堆

但其实不用这么麻烦:直接根据 \(\text{cnt}\) 值判断是否已经被合并然后选择性跳过即可。

最后做到 1 号点就不用重新插入堆中了。

Code

/*
* Author : _Wallace_
* Source : https://www.cnblogs.com/-Wallace-/
* Problem : AtCoder AGC023F 01 on Tree
*/
#include <algorithm>
#include <iostream>
#include <queue> using namespace std;
const int N = 2e5 + 5; int n, fa[N], dsu[N];
int cnt[N][2]; struct item {
int c0, c1, idx;
bool operator < (const item& t) const {
return c0 * 1ll * t.c1 < c1 * 1ll * t.c0;
}
};
priority_queue<item> pq; int find(int x) {
return x == dsu[x] ? x : dsu[x] = find(dsu[x]);
} signed main() {
ios::sync_with_stdio(false); cin >> n;
for (register int i = 2; i <= n; i++)
cin >> fa[i];
for (register int i = 1, val; i <= n; i++)
cin >> val, cnt[i][val]++;
for (register int i = 1; i <= n; i++)
dsu[i] = i; long long ans = 0;
for (register int i = 2; i <= n; i++)
pq.push({cnt[i][0], cnt[i][1], i}); while (!pq.empty()) {
item cur = pq.top(); pq.pop();
int x = find(cur.idx), c0 = cur.c0, c1 = cur.c1; if (cnt[x][0] != c0 || cnt[x][1] != c1)
continue; int y = find(fa[x]);
ans += cnt[y][1] * 1ll * cnt[x][0];
cnt[y][0] += cnt[x][0];
cnt[y][1] += cnt[x][1]; dsu[x] = y;
if (y > 1) pq.push({cnt[y][0], cnt[y][1], y});
} cout << ans << endl;
return 0;
}

【AtCoder AGC023F】01 on Tree(贪心)的更多相关文章

  1. 【luogu AT3957】[AGC023F] 01 on Tree

    01 on Tree 题目链接:luogu AT3957 题目大意 有一棵根为 \(1\) 的树,每个节点有个值 \(0\) 或 \(1\). 然后每次你可以把一个没有父亲的点删除,然后把值放进一个数 ...

  2. [atcoder contest 010] F - Tree Game

    [atcoder contest 010] F - Tree Game Time limit : 2sec / Memory limit : 256MB Score : 1600 points Pro ...

  3. AtCoder Grand Contest 023 F - 01 on Tree

    Description 题面 Solution HNOI-day2-t2 复制上去,删点东西,即可 \(AC\) #include<bits/stdc++.h> using namespa ...

  4. D - Digging(01背包,贪心)

    D - Digging Time Limit:2000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu Submit St ...

  5. AtCoder ABC 070D - Transit Tree Path

    传送门:http://abc070.contest.atcoder.jp/tasks/abc070_d 本题是一个图论问题——树(Tree). 有一棵结点数目为n的无向树.第i条边连接结点ai与bi, ...

  6. $POJ$2976 $Dropping\ tests$ 01分数规划+贪心

    正解:01分数规划 解题报告: 传送门! 板子题鸭,,, 显然考虑变成$a[i]-mid\cdot b[i]$,显然无脑贪心下得选出最大的$k$个然后判断是否大于0就好(,,,这么弱智真的算贪心嘛$T ...

  7. HDU 4825 Xor Sum(经典01字典树+贪心)

    Xor Sum Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 132768/132768 K (Java/Others) Total ...

  8. Poj 2499 Binary Tree(贪心)

    题目链接:http://poj.org/problem?id=2499 思路分析:结点向左边移动时结点(a, b)变为( a+b, b),向右边移动时( a, b )变为( a, a + b); 为求 ...

  9. POJ 2054 Color a Tree#贪心(难,好题)

    题目链接 代码借鉴此博:http://www.cnblogs.com/vongang/archive/2011/08/19/2146070.html 其中关于max{c[fa]/t[fa]}贪心原则, ...

随机推荐

  1. 《GNU_Makefile》第4章——makefile规则

    规则明确在什么情况下,使用什么方法,重构文件,该文件称为目标. make的唯一目的是重构终极目标.终极目标默认是第一个目标. 1. 2.规则语法 TARGETS : PREREQUISITES COM ...

  2. 为什么删除的Ceph对象还能get

    前言 在很久以前在研究一套文件系统的时候,当时发现一个比较奇怪的现象,没有文件存在,磁盘容量还在增加,在研究了一段时间后,发现这里面有一种比较奇特的处理逻辑 这套文件系统在处理一个文件的时候放入的是一 ...

  3. SQL SERVER中 DBLINK的实现

    不同服务器数据库之间的数据操作 --创建链接服务器 exec sp_addlinkedserver'ITSV' ,'' , 'SQLOLEDB' ,'远程服务器名或ip地址' exec sp_addl ...

  4. 创建Spring Cloud聚合项目

    使用maven创建单一项目的时候通常用不到聚合项目,创建spring cloud项目时候,由于下面都是一个一个微服务,每个服务对应一个项目,这就需要用到聚合项目,方便对依赖和项目之间的关系进行管理,使 ...

  5. abp(net core)+easyui+efcore实现仓储管理系统——出库管理之六(五十五)

    abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统--ABP总体介绍(一) abp(net core)+ ...

  6. Linux提权(持续更新)

    利用/etc/passwd提权 个人认为,这种提权方式在现实场景中难以实现,条件太过苛刻,但是建立Linux下的隐藏账户是个不错的选择,灵感来自:https://www.hackingarticles ...

  7. [i春秋]“百度杯”CTF比赛 十月场-Hash

    前言 涉及知识点:反序列化.代码执行.命令执行 题目来自:i春秋 hash  如果i春秋题目有问题可以登录榆林学院信息安全协会CTF平台使用 或者利用本文章提供的源码自主复现 [i春秋]"百 ...

  8. webug第十三关:XSS

    第十三关:XSS 点击链接 xss,弹框框

  9. 使用Beyond Compare对比大篇幅文件的小技巧

    我们在编辑较大篇幅的文章时,无可避免地要经过多次的修改.有时候修改的版本过多时,我们很可能就会记不清最新版的文章对比上一版的文章,究竟修改了哪些地方.但有了Beyond Compare(Windows ...

  10. CorelDRAW常用工具之涂抹工具

    CDR作为绘图软件或者说平面设计软件使用频繁的功能之一,就是为绘制好的图片进行涂抹混色. 1.基本操作 CorelDRAW平面设计软件的涂抹工具是在形状工具组里的,打开左侧工具栏"形状&qu ...