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_ ...
随机推荐
- Cocos Creator 获得设备分辨率
var b = cc.director.getWinSizeInPixels() var bx = b.width var by = b.height
- DataGridView控件用法二:常用属性
通常会设置的DataGridView的属性如下: AllowUserToAddRows - False指示是否向用户显示用于添加行的选项,列标题下面的一行空行将消失.一般让其消失.AllowUserT ...
- EL的隐含对象(一)【页面上下文对象】
页面上下文对象为pageContext,用于访问JSP内置对象(例如:request.response.out.session.exception.page等)和ServletContext.在获取到 ...
- C# Mongo Client 2.4.2创建索引
static async Task CreateIndex() { var client = new MongoClient(); var database = client.GetDatabase( ...
- html中通过js获取接口JSON格式数据解析以及跨域问题
前言:本人自学前端开发,一直想研究下js获取接口数据在html的实现,顺利地找到了获取数据的方法,但是有部分接口在调用中出现无法展示数据.经查,发现时跨域的问题,花费了一通时间,随笔记录下过程,以方便 ...
- Unknown Treasure (卢卡斯 + 孙子定理, 模板题)
Unknown Treasure 参考链接 : https://www.cnblogs.com/linyujun/p/5199684.html 卢卡斯定理 : C(n, m) % p = C(n ...
- 向SQL Server中导入Excel的数据
1. 手动界面导入Excel数据 同 https://jingyan.baidu.com/article/ce09321b9a0e252bff858ff9.html 首先打开并登陆sql serve ...
- Palindrome Bo (预处理 + 区间DP)
先进行离散化,然后再预处理出所有位置的下一个元素,做好这一步对时间的优化非常重要. 剩下的就是一般的DP了.区间DP #include<bits/stdc++.h> using names ...
- redis 缓存刷新
- AtCoder Beginner Contest 084(AB)
A - New Year 题目链接:https://abc084.contest.atcoder.jp/tasks/abc084_a Time limit : 2sec / Memory limit ...