题目传送门

  传送点I

  传送点II

  传送点III

题目大意

  给定一颗有$n$个点的树,$i$号点的权值是$2^{i}$要求删去$k$个点,使得剩下的点仍然连通,并且总权值和最大,问删去的所有点的编号。

  其实这道题和虚树没有太大的关系,我只是提一下而已。

  因为点的权值很特殊,所以相当于要求剩下序列(从大到小)的字典序最大。

  然后过程就比较显然了。从大到小枚举点,判断能否保留它(计算加入它后,新的树的点数有没有超过限制)。保留它是指和已经被保留的点连通。

  这个相当于使得一些关键点连通,然后求出总点数。显然它可以用虚树来做。所以考虑虚树的构造算法,于是我们成功得到了一个时间复杂度为一个天文数字的算法。

  将一个关键点添加进已有的树(暂时把保留的点形成的树这么称呼吧)中,无非情况有两种:

  • 从这个关键点往上跳,直到跳到某个在已有的树中的点,经过点均被加入已有的树中。这种情况出现当且仅当关键点存在于已有的树的根(离原树钦定的根最近的一个点)在原树的子树中。
  • 这个关键点到已有的树的根的路径上所有点被加入已有的树。(其他情况)

  显然,这个过程不能纯暴力做。首先需要计算新添加的点的个数,如果能够保留它,那么暴力修改(因为修改的节点数等于$(n - k)$)。

  对于情况一,可以通过记录深度数组和倍增数组解决。

  对于情况二,求完LCA用树上距离公式。

  总时间复杂度$O(n\log n)$

Code

 /**
* Codeforces
* Problem#980E
* Accepted
* Time: 1918ms
* Memory: 172700k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = 1e6 + , bzmax = ; int n, m;
int cnt;
vector<int> *g;
int *in, *out;
int *dep;
boolean *vis;
int bz[N][bzmax]; inline void init() {
scanf("%d%d", &n, &m);
m = n - m;
g = new vector<int>[(n + )];
in = new int[(n + )];
out = new int[(n + )];
dep = new int[(n + )];
vis = new boolean[(n + )];
memset(vis, false, sizeof(boolean) * (n + ));
for (int i = , u, v; i < n; i++) {
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
} void dfs(int p, int fa) {
in[p] = ++cnt, dep[p] = dep[fa] + ;
bz[p][] = fa;
for (int i = ; i < bzmax; i++)
bz[p][i] = bz[bz[p][i - ]][i - ];
for (int i = ; i < (signed)g[p].size(); i++) {
int e = g[p][i];
if (e == fa) continue;
dfs(e, p);
}
out[p] = cnt;
} int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
if (in[u] <= in[v] && out[u] >= out[v])
return u;
for (int i = bzmax - , a; ~i; i--) {
a = bz[u][i];
if (!(in[a] <= in[v] && out[a] >= out[v]))
u = a;
}
return bz[u][];
} inline void solve() {
dep[] = , vis[n] = true, m -= , in[] = , out[] = ;
dfs(, );
int vr = n;
for (int i = n - ; i; i--) {
if (vis[i]) continue;
if (in[vr] <= in[i] && out[vr] >= out[i]) {
int u = i, v;
for (int j = bzmax - ; ~j; j--) {
v = bz[u][j];
if (dep[v] >= dep[vr] && !vis[v])
u = v;
}
if (dep[i] - dep[u] + > m) continue;
for (int j = i; j && !vis[j]; j = bz[j][])
vis[j] = true, m--;
} else {
int g = lca(vr, i);
// cerr << dep[i] + dep[vr] - (dep[g] << 1) << endl;
if (dep[i] + dep[vr] - (dep[g] << ) > m) continue;
for (int j = i; j != bz[g][] && !vis[j]; j = bz[j][])
vis[j] = true, m--;
for (int j = bz[vr][]; !vis[j]; j = bz[j][])
vis[j] = true, m--;
vr = g;
}
}
for (int i = ; i <= n; i++)
if (!vis[i])
printf("%d ", i);
} int main() {
init();
solve();
return ;
}

Multiplication algorithm

  我们完美解决了这个问题?不。做人要有追求,1.9s真不爽。

  注意到$n$号点必定存在于已有的树中。那么直接钦定$n$号点作为原树的根。

  这样直接消灭掉了情况二。对于情况一,也可以稍微改一改。因为原树的根与已有的树的根重合,可以直接树差分加上树状数组计算出距离。

Code

 /**
* Codeforces
* Problem#980E
* Accepted
* Time: 888ms
* Memory: 98300k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; typedef class IndexedTree {
public:
int *ar;
int s; IndexedTree():ar(NULL) { }
IndexedTree(int s):s(s) {
ar = new int[(s + )];
memset(ar, , sizeof(int) * (s + ));
} void add(int idx, int val) {
for ( ; idx <= s; idx += (idx & (-idx)))
ar[idx] += val;
} int getSum(int idx) {
int rt = ;
for ( ; idx; idx -= (idx & (-idx)))
rt += ar[idx];
return rt;
} void add(int l, int r, int val) {
add(l, val), add(r + , -val);
}
}IndexedTree; int n, m;
int cnt;
vector<int> *g;
int *in, *out;
int *dep, *fas;
boolean *vis;
IndexedTree it; inline void init() {
scanf("%d%d", &n, &m);
m = n - m;
g = new vector<int>[(n + )];
in = new int[(n + )];
out = new int[(n + )];
dep = new int[(n + )];
fas = new int[(n + )];
vis = new boolean[(n + )];
it = IndexedTree(n);
memset(vis, false, sizeof(boolean) * (n + ));
for (int i = , u, v; i < n; i++) {
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
} void dfs(int p, int fa) {
in[p] = ++cnt, dep[p] = dep[fa] + , fas[p] = fa;
for (int i = ; i < (signed)g[p].size(); i++) {
int e = g[p][i];
if (e == fa) continue;
dfs(e, p);
}
out[p] = cnt;
} inline void solve() {
dep[] = ;
dfs(n, );
vis[n] = true, it.add(in[n], out[n], ), m--;
for (int i = n - ; i; i--) {
if (vis[i]) continue;
int added = dep[i] - it.getSum(in[i]);
if (added > m) continue;
for (int j = i; !vis[j]; j = fas[j])
it.add(in[j], out[j], ), vis[j] = true, m--;
}
for (int i = ; i <= n; i++)
if (!vis[i])
printf("%d ", i);
} int main() {
init();
solve();
return ;
}

  

Codeforces 980E The Number Games - 贪心 - 树状数组的更多相关文章

  1. Codeforces 980E The Number Games 贪心 倍增表

    原文链接https://www.cnblogs.com/zhouzhendong/p/9074226.html 题目传送门 - Codeforces 980E 题意 $\rm Codeforces$ ...

  2. 【bzoj4240】有趣的家庭菜园 贪心+树状数组

    题目描述 对家庭菜园有兴趣的JOI君每年在自家的田地中种植一种叫做IOI草的植物.JOI君的田地沿东西方向被划分为N个区域,由西到东标号为1~N.IOI草一共有N株,每个区域种植着一株.在第i个区域种 ...

  3. codeforces 1249 D2 Too Many Segments (hard version) 贪心+树状数组

    题意 给定n个线段,线段可以相交,第\(i\)个线段覆盖的区间为\([l_i,r_i]\),问最少删除多少个线段让覆盖每个点的线段数量小于等于k. 分析 从左往右扫每个点\(x\),若覆盖点\(x\) ...

  4. Codeforces Gym 100114 H. Milestones 离线树状数组

    H. Milestones Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Descripti ...

  5. 贪心+树状数组维护一下 Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) D

    http://codeforces.com/contest/724/problem/D 题目大意:给你一个串,从串中挑选字符,挑选是有条件的,按照这个条件所挑选出来的字符集合sort一定是最后选择当中 ...

  6. Codeforces - 828E DNA Evolution —— 很多棵树状数组

    题目链接:http://codeforces.com/contest/828/problem/E E. DNA Evolution time limit per test 2 seconds memo ...

  7. Codeforces Gym 100269F Flight Boarding Optimization 树状数组维护dp

    Flight Boarding Optimization 题目连接: http://codeforces.com/gym/100269/attachments Description Peter is ...

  8. Codeforces 570D TREE REQUESTS dfs序+树状数组 异或

    http://codeforces.com/problemset/problem/570/D Tree Requests time limit per test 2 seconds memory li ...

  9. UVALive 6911---Double Swords(贪心+树状数组(或集合))

    题目链接 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...

随机推荐

  1. Ecshop表结构 order_info

    CREATE TABLE IF NOT EXISTS `ecs_order_info` (  `order_id` mediumint(8) unsigned NOT NULL AUTO_INCREM ...

  2. 在webpack自定义配置antd的按需加载和修改主题色

    最近使用antd来做react项目的UI.从antd官网上,在使用create-react-app脚手架搭建项目时步骤如下: (1)添加模块 react-app-rewired, babel-plug ...

  3. javaweb + tomcat + 部署 + 域名绑定 + 默认首页

    ①:把javaweb项目打包成war(不会的自行百度) ②:把war拷贝到服务器的tomcat里面的webapps下 ③:到bin文件夹下.bat文件启动tomcat,启动后会解压war包 ⑤:解压后 ...

  4. hive中安装hive_utils模块

    1. 因为在linux部署的python 3.6 在安装模块的时候遇到了许多问题,所以使用linux中的python3.6环境 2. 首先使用pip安装 hive_utils 模块sudo pip i ...

  5. 在linux中配置mysql的过程

    以华为企业云中的CentOS 7 为例. 1. 首先要安装相应的包,这个使用各种linux发行版的包管理工具就行,不再赘述.有一点需要注意,现在许多linux发行版将mariadb作为MySQL的默认 ...

  6. ubuntu安装rvm

    sudo apt-get install curl git-core bash -s stable < <(curl -s https://raw.github.com/wayneeseg ...

  7. git分支流

    ## 新建一个iss1分支 $ git branch iss1 ## 切换到iss1分支 $ git checkout iss1 Switched to branch 'iss1' ## 查看分支,当 ...

  8. 常用bash,autoUserAdd.sh

    #!/bin/bash # auth: xiluhua # date: -- read -p "please input a username:" username [ -z $u ...

  9. linux常用命令:cp 命令

    cp命令用来复制文件或者目录,是Linux系统中最常用的命令之一.一般情况下,shell会设置一个别名,在命令行下复制文件时,如果目标文件已经存在,就会询问是否覆盖,不管你是否使用-i参数.但是如果是 ...

  10. Django MTV simple_tag filter inclusion_tag

    Django框架 模型(Model).视图(View)和控制器(Controller),具有耦合性低.重用性高.生命周期成本低等优点. MVC 框架 --  Model -View -Controll ...