Codeforces 980E The Number Games - 贪心 - 树状数组

时间:2021-07-12 22:45:57

题目传送门

  传送点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】有趣的家庭菜园 贪心&plus;树状数组

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

  3. codeforces 1249 D2 Too Many Segments &lpar;hard version&rpar; 贪心&plus;树状数组

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

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

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

  5. 贪心&plus;树状数组维护一下 Intel Code Challenge Final Round &lpar;Div&period; 1 &plus; Div&period; 2&comma; Combined&rpar; 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序&plus;树状数组 异或

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

  9. UVALive 6911---Double Swords&lpar;贪心&plus;树状数组(或集合)&rpar;

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

随机推荐

  1. jquery mobile

    页面:data-role="page"  header.content.fooder 过渡:data-transition ="slide"  反向过渡:dat ...

  2. python之import机制

    1. 标准 import        Python 中所有加载到内存的模块都放在 sys.modules .当 import 一个模块时首先会在这个列表中查找是否已经加载了此模块,如果加载了则只是将 ...

  3. DISTINCT后按照DISTINCT之前的某列进行排序

    SELECT 行业名称 FROM 评分标准 GROUP BY 行业名称 ORDER BY MAX(行业ID) DESC

  4. Data Flow -&gt&semi;&gt&semi; CDC Control Task&comma; CDC Source&comma; CDC Splitter

    CDC Control Task可以从控制CDC数据同步,比如初始化加载.LSN范围的管理.它可以代替另一种做法,就是通过调用一批CDC函数来完成同样的事情.从SSIS的角度来完成,事情编程简单,和另 ...

  5. Android 获取TextView 显示的字符串宽度

    工作上有业务需要判断textview是否换行,我的做法是判断textview要显示的字符串的宽度是否超过我设定的宽度,若超过则会执行换行. 项目中的其他地方也有这样的需求,故直接使用了那一块的代码.如 ...

  6. iOS开发之第三方登录QQ -- 史上最全最新第三方登录QQ方式实现

    项目地址 :  https://github.com/zhonggaorong/QQLoginDemo/tree/master 最新版本的qq登录实现步骤实现: 1. 首先,你需要去向腾讯申请账号. ...

  7. AngularJS的五个超酷特性

    AngularJS是一个超棒的javascript框架,不单单对于开发人员来说非常有吸引力,对于UI设计师来说也同样出色.在这篇教程中,我们将简单的介绍AngularJS几个重量级必备特性,并且介绍它 ...

  8. 《剑指Offer》附加题&lowbar;用两个队列实现一个栈&lowbar;C&plus;&plus;版

    在<剑指Offer>中,在栈和队列习题中,作者留下来一道题目供读者自己实现,即"用两个队列实现一个栈". 在计算机数据结构中,栈的特点是后进先出,即最后被压入(push ...

  9. 教你上传本地代码到github转载

    原创 2015年07月03日 10:47:13 标签: 上传代码github   转载请标明出处: http://blog.csdn.net/hanhailong726188/article/deta ...

  10. git简单提交操作

    一.本地仓库操作 1.打开git命令行,先'到需要提交的目录 2.输入git init,初始化本地仓库 3.输入git add <file> 命令添加提交文件 4.输入git status ...