关于此题Ethflow Round 1 (Codeforces Round 1001, Div. 1 + Div. 2)_E1. The Game (Easy Version)的一些总结
- 题目大意:(就不说了
思路
- 要求如果一个人无法操作则这个人获胜,由于这个版本只要求我们输出一个满足题意的点即可,那么我们可以贪心的进行思考。在最理想的情况下,如果我们此时选择了所有权值当中第二大的节点,并且删掉这个节点对应子树之后还有权值最大的节点,那么第二个人只能选择权值最大的节点,再轮到第一个人时便无法操作从而取胜。而如果删掉权值第二大的节点对应子树之后,权值最大的节点也都没了,这时候我们就要再往权值小的方向找。在这种情况下我们可以理解为,所有权值最大的节点都在权值第二大的节点的子树中,那么此时如果我们选权值第三大的节点,并且删掉权值第三大的节点之后还剩下最大或者第二大的节点,那么我们就可以取胜,因为进入到这种情况的前提是所有权值最大的节点都在权值第二大的节点的子树中。那如果删掉权值第三大的节点依旧不行呢?我们发现这个问题就可以遍历权值来解决,如果遍历完所有的权值依旧没有得到答案,那么就说明不可能赢,输出0即可。
- 现在的问题就是:如何知道某一个权值对应的所有节点是不是都在另一个权值对应某一节点的子树中,即以一个节点为根的子树外是否有比这个节点权值更大的节点。这里用到了时间戳。我们记录下访问到每个节点的时间和访问完每个节点子树的时间,然后从大到小遍历权值对应节点同时,判断比这个节点权值更大的所有节点中出现的最早时间和最晚时间是不是在当前节点的出现时间和子树访问完时间之外,如果是那么就说明肯定是有比这个节点权值更大的节点在此节点子树之外,并且是第一次出现这种情况,我们直接输出答案后return即可。
代码
#include<bits/stdc++.h>
using namespace std;
int t;
const int N = 5e5 + 10;
int n,tim,dfn[N],low[N];
int head[2 * N],tot;
struct node {
int v,next;
}cnt[2 * N];
struct Node {
int w,pos;
bool operator < (const Node &a) const {
return w > a.w;
}
}in[N];
void insert(int u,int v) {
cnt[++tot].v = v;
cnt[tot].next = head[u];
head[u] = tot;
}
void dfs(int u,int fa) {
dfn[u] = ++tim;
for(int i = head[u];i;i = cnt[i].next) {
if(cnt[i].v == fa) continue;
dfs(cnt[i].v,u);
}
low[u] = tim;
}
void solve() {
for(int i = 0;i <= n;i++) dfn[i] = low[i] = head[i] = 0;
for(int i = 0;i <= tot;i++) cnt[i].v = cnt[i].next = 0;
tot = 0;
cin >> n;
for(int i = 1;i <= n;i++) cin >> in[i].w,in[i].pos = i;
for(int i = 1,u,v;i < n;i++) {
cin >> u >> v;
insert(u,v);
insert(v,u);
}
sort(in + 1,in + 1 + n);
tim = 0;
dfs(1,0);
int ll = dfn[in[1].pos],rr = dfn[in[1].pos];
int tl = ll,tr = rr;
for(int i = 2;i <= n;i++) {
if(in[i].w != in[i - 1].w) {
ll = min(ll,tl);
rr = max(rr,tr);
tl = 0;
tr = 0;
}
if(in[i].w < in[1].w) {
if(ll < dfn[in[i].pos] || rr > low[in[i].pos]) {
cout << in[i].pos << '\n';
return;
}
}
tl = tl == 0 ? dfn[in[i].pos] : min(tl,dfn[in[i].pos]);
tr = tr == 0 ? dfn[in[i].pos] : max(tr,dfn[in[i].pos]);
}
cout << 0 << '\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin >> t;
while(t--) solve();
return 0;
}
拓展
- 再多提一嘴关于dfs序、时间戳和欧拉序。dfs序即按照深度优先遍历访问的节点的顺序,这个序列是不唯一的,要求每访问到一个节点就将其加入序列(回溯到当前节点也算一次访问)。dfs序对应的性质即每个节点出现两次对应序列中区间即该节点对应子树。由于每个节点会被统计两次,对应序列长度为
。 - 时间戳很好理解,即上述所说的每个节点第一次出现的位置,注意灵活运用。
- 欧拉序也是按照深度优先遍历访问节点的顺序,但是不同的是,dfs序是遍历完所有的子树后再回到根,而欧拉序是遍历完一棵子树后先回到根再遍历别的子树。每个点的访问次数是其度,由于每条边会被访问两次,并且第一个节点从根开始会使其被多访问一次,所以序列长度是
。其应用通常是lca方面,两个节点第一次出现位置之间的深度最小的节点就是它们的lca。那么此时求lca问题就可以转化为RMQ问题。与倍增求lca有利有弊,主要看对查询操作要求的时间复杂度。
分类:
做题总结