Codeforces 980E The Number Games - 贪心 - 树状数组
其实这道题和虚树没有太大的关系,我只是提一下而已。
因为点的权值很特殊,所以相当于要求剩下序列(从大到小)的字典序最大。
然后过程就比较显然了。从大到小枚举点,判断能否保留它(计算加入它后,新的树的点数有没有超过限制)。保留它是指和已经被保留的点连通。
这个相当于使得一些关键点连通,然后求出总点数。显然它可以用虚树来做。所以考虑虚树的构造算法,于是我们成功得到了一个时间复杂度为一个天文数字的算法。
将一个关键点添加进已有的树(暂时把保留的点形成的树这么称呼吧)中,无非情况有两种:
- 从这个关键点往上跳,直到跳到某个在已有的树中的点,经过点均被加入已有的树中。这种情况出现当且仅当关键点存在于已有的树的根(离原树钦定的根最近的一个点)在原树的子树中。
- 这个关键点到已有的树的根的路径上所有点被加入已有的树。(其他情况)
显然,这个过程不能纯暴力做。首先需要计算新添加的点的个数,如果能够保留它,那么暴力修改(因为修改的节点数等于$(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 - 贪心 - 树状数组的更多相关文章
- Codeforces 980E The Number Games 贪心 倍增表
原文链接https://www.cnblogs.com/zhouzhendong/p/9074226.html 题目传送门 - Codeforces 980E 题意 $\rm Codeforces$ ...
- 【bzoj4240】有趣的家庭菜园 贪心+树状数组
题目描述 对家庭菜园有兴趣的JOI君每年在自家的田地中种植一种叫做IOI草的植物.JOI君的田地沿东西方向被划分为N个区域,由西到东标号为1~N.IOI草一共有N株,每个区域种植着一株.在第i个区域种 ...
- codeforces 1249 D2 Too Many Segments (hard version) 贪心+树状数组
题意 给定n个线段,线段可以相交,第\(i\)个线段覆盖的区间为\([l_i,r_i]\),问最少删除多少个线段让覆盖每个点的线段数量小于等于k. 分析 从左往右扫每个点\(x\),若覆盖点\(x\) ...
- Codeforces Gym 100114 H. Milestones 离线树状数组
H. Milestones Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Descripti ...
- 贪心+树状数组维护一下 Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) D
http://codeforces.com/contest/724/problem/D 题目大意:给你一个串,从串中挑选字符,挑选是有条件的,按照这个条件所挑选出来的字符集合sort一定是最后选择当中 ...
- Codeforces - 828E DNA Evolution —— 很多棵树状数组
题目链接:http://codeforces.com/contest/828/problem/E E. DNA Evolution time limit per test 2 seconds memo ...
- Codeforces Gym 100269F Flight Boarding Optimization 树状数组维护dp
Flight Boarding Optimization 题目连接: http://codeforces.com/gym/100269/attachments Description Peter is ...
- Codeforces 570D TREE REQUESTS dfs序+树状数组 异或
http://codeforces.com/problemset/problem/570/D Tree Requests time limit per test 2 seconds memory li ...
- UVALive 6911---Double Swords(贪心+树状数组(或集合))
题目链接 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...
随机推荐
- HTML5 缓存
一.在html/htm文件中声明缓存,声明方式: <!DOCTYPE HTML> <html manifest="demo.appcache">...3 & ...
- ReentrantLock源码(二)
一.ReentrantLock类中的方法解读. 1.lock方法.实现了接口Lock中的lock方法.这里实际上是调用了sync成员变量的lock方法来实现.所以取决于sync的实现. 2.unloc ...
- 强化学习---A3C
Asynchronous Advantage Actor-Critic (A3C) 在RL任务中,我们本质上最终要学习的是策略(Policy) value-based方法:间接方法,即通过学习值函数( ...
- web.config 特殊字符转义
字符 转义码 & 符号 & & 单引号 ' ' 双引号 " " 大于 > > 小于 < <
- UML之组件图
基本概念:组件图即是用来描述组件与组件之间关系的一种UML图.组件图在宏观层面上显示了构成系统某一个特定方面的实现结构. 组件图中主要包含三种元素,即组件.接口和关系. 组件图通过这些元素描述了系统的 ...
- FILE文件删除操作(删除指定文件夹下所有文件和文件夹包括子文件夹下所有文件和文件夹),就是删除所有
2018-11-05 19:42:08开始写 选择 删除 1.FileUtils.java类 import java.io.File;//导入包 import java.util.List;//导入 ...
- codeforces 980E The Number Games
题意: 给出一棵树,要求去掉k个点,使得剩下的还是一棵树,并且要求Σ(2^i)最大,i是剩下的节点的编号. 思路: 要使得剩下的点的2的幂的和最大,那么肯定要保住大的点,这是贪心. 考虑去掉哪些点的话 ...
- mybatis源码解析1--前言
在开始分析mybatis源码之前,需要定一个目标,也就是我们不是为了读源码而去读,一定是带着问题去读,在读的时候去寻找到答案,然后再读码的同时整理总结,学习一些高级的编码方式和技巧. 首先我们知道my ...
- flask模板的基本用法(定界符、模板语法、渲染模板),模板辅助工具(上下文、全局对象、过滤器、测试器、模板环境对象)
flask模板 在动态web程序中,视图函数返回的HTML数据往往需要根据相应的变量(比如查询参数)动态生成. 当HTML代码保存到单独的文件中时,我们没法再使用字符串格式化或拼接字符串的当时在HTM ...
- GAN的文献综述
1.Conditional Generative Adversarial Netwoks Describe GAN: Generative adversarial nets were recently ...