题目传送门

  传送点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. hibernate.validator.constraints.NotEmpty校验请求参数报错java.lang.NoClassDefFoundError: javax/el/PropertyNotFoundException

    spring maven项目,使用hibernate validator 注解形式校验客户端的请求参数. hibernate-validator版本:5.0.2.Final validation-ap ...

  2. 强力上攻后,缓解期结束,MACD死叉的案例

    eg1.顶部,MACD收紧,缓解期刚过,正好下M5,触发减仓条件

  3. HDU 3306 Another kind of Fibonacci(矩阵+ll超时必须用int&输入必须取模&M必须是int类型)

    Another kind of Fibonacci [题目链接]Another kind of Fibonacci [题目类型]矩阵+ll超时必须用int&输入必须取模&M必须是int ...

  4. Javascript-全局函数和局部函数作用域的理解

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. java中的锁之Lock接口与Condition接口

    一.Lock源码. 1.是一个接口.一共有6个方法. 2.方法详细如下: (1)当前线程尝试获取锁.结果分两种情况,一是成功获取到锁,则返回:二是获取锁失败,则一直等待.不响应中断请求. (2)当前线 ...

  6. 《大话设计模式》c++实现 外观模式

    外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 外观模式在什么时候使用呢? 分为三个阶段: (1)首先,在设计初期阶段,应该要有意识的 ...

  7. python django简单的登陆实现

    实现方法: 1,可以先定义一个基础的页面访问路径 例如:http://127.0.0.1:8000/index/  定义index路径 在urls urlpatterns = [ url(r'^ind ...

  8. html5-section元素

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  9. 【转】 如何导入excel数据到数据库,并解决导入时间格式问题

    在办公环境下,经常会用到处理excel数据,如果用写程序导入excel数据到数据库那就太麻烦了,涉及解析excel,还要各种格式问题,下面简单利用数据库本身支持的功能解决这类导入问题. 准备 创建表 ...

  10. Subway (树中心 + 树hash)

    首先找到树的中心或者中心,我这里是找中心,因为我们需要找一个相同的起点,然后最多2个中心就是树的宽度为偶数时,奇数时为1个. 找到之后需要对树进行hash,使得每个点都具备独特性,使之树的形态能够保证 ...