zoukankan      html  css  js  c++  java
  • Codeforces 980E The Number Games

    题目传送门

      传送点I

      传送点II

      传送点III

    题目大意

       给定一颗有$n$个点的树,$i$号点的权值是$2^{i}$要求删去$k$个点,使得剩下的点仍然连通,并且总权值和最大,问删去的所有点的编号。

      其实这道题和虚树没有太大的关系,我只是提一下而已。

      因为点的权值很特殊,所以相当于要求剩下序列(从大到小)的字典序最大。

      然后过程就比较显然了。从大到小枚举点,判断能否保留它(计算加入它后,新的树的点数有没有超过限制)。保留它是指和已经被保留的点连通。

      这个相当于使得一些关键点连通,然后求出总点数。显然它可以用虚树来做。所以考虑虚树的构造算法,于是我们成功得到了一个时间复杂度为一个天文数字的算法。

      将一个关键点添加进已有的树(暂时把保留的点形成的树这么称呼吧)中,无非情况有两种:

    • 从这个关键点往上跳,直到跳到某个在已有的树中的点,经过点均被加入已有的树中。这种情况出现当且仅当关键点存在于已有的树的根(离原树钦定的根最近的一个点)在原树的子树中。
    • 这个关键点到已有的树的根的路径上所有点被加入已有的树。(其他情况)

      显然,这个过程不能纯暴力做。首先需要计算新添加的点的个数,如果能够保留它,那么暴力修改(因为修改的节点数等于$(n - k)$)。

      对于情况一,可以通过记录深度数组和倍增数组解决。

      对于情况二,求完LCA用树上距离公式。

      总时间复杂度$O(nlog n)$

    Code

     1 /**
     2  * Codeforces
     3  * Problem#980E
     4  * Accepted
     5  * Time: 1918ms
     6  * Memory: 172700k
     7  */
     8 #include <bits/stdc++.h>
     9 using namespace std;
    10 typedef bool boolean;
    11 
    12 const int N = 1e6 + 5, bzmax = 21;
    13 
    14 int n, m;
    15 int cnt;
    16 vector<int> *g;
    17 int *in, *out;
    18 int *dep;
    19 boolean *vis;
    20 int bz[N][bzmax];
    21 
    22 inline void init() {
    23     scanf("%d%d", &n, &m);
    24     m = n - m;
    25     g = new vector<int>[(n + 1)];
    26     in = new int[(n + 1)];
    27     out = new int[(n + 1)];
    28     dep = new int[(n + 1)];
    29     vis = new boolean[(n + 1)];
    30     memset(vis, false, sizeof(boolean) * (n + 1));
    31     for (int i = 1, u, v; i < n; i++) {
    32         scanf("%d%d", &u, &v);
    33         g[u].push_back(v);
    34         g[v].push_back(u);
    35     }
    36 }
    37 
    38 void dfs(int p, int fa) {
    39     in[p] = ++cnt, dep[p] = dep[fa] + 1;
    40     bz[p][0] = fa;
    41     for (int i = 1; i < bzmax; i++)
    42         bz[p][i] = bz[bz[p][i - 1]][i - 1];
    43     for (int i = 0; i < (signed)g[p].size(); i++) {
    44         int e = g[p][i];
    45         if (e == fa)    continue;
    46         dfs(e, p);
    47     }
    48     out[p] = cnt;
    49 }
    50 
    51 int lca(int u, int v) {
    52     if (dep[u] < dep[v])    swap(u, v);
    53     if (in[u] <= in[v] && out[u] >= out[v])
    54         return u;
    55     for (int i = bzmax - 1, a; ~i; i--) {
    56         a = bz[u][i];
    57         if (!(in[a] <= in[v] && out[a] >= out[v]))
    58             u = a;
    59     }
    60     return bz[u][0];
    61 }
    62 
    63 inline void solve() {
    64     dep[0] = 0, vis[n] = true, m -= 1, in[0] = 0, out[0] = 1428571;
    65     dfs(1, 0);
    66     int vr = n;
    67     for (int i = n - 1; i; i--)    {
    68         if (vis[i])    continue;
    69         if (in[vr] <= in[i] && out[vr] >= out[i]) {
    70             int u = i, v;
    71             for (int j = bzmax - 1; ~j; j--) {
    72                 v = bz[u][j];
    73                 if (dep[v] >= dep[vr] && !vis[v])
    74                     u = v;
    75             }
    76             if (dep[i] - dep[u] + 1 > m)    continue;
    77             for (int j = i; j && !vis[j]; j = bz[j][0])
    78                 vis[j] = true, m--;
    79         } else {
    80             int g = lca(vr, i);
    81 //            cerr << dep[i] + dep[vr] - (dep[g] << 1) << endl;
    82             if (dep[i] + dep[vr] - (dep[g] << 1) > m)    continue;
    83             for (int j = i; j != bz[g][0] && !vis[j]; j = bz[j][0])
    84                 vis[j] = true, m--;
    85             for (int j = bz[vr][0]; !vis[j]; j = bz[j][0])
    86                 vis[j] = true, m--;
    87             vr = g;
    88         }
    89     }
    90     for (int i = 1; i <= n; i++)
    91         if (!vis[i])
    92             printf("%d ", i);
    93 }
    94 
    95 int main() {
    96     init();
    97     solve();
    98     return 0;
    99 }
    Multiplication algorithm

      我们完美解决了这个问题?不。做人要有追求,1.9s真不爽。

      注意到$n$号点必定存在于已有的树中。那么直接钦定$n$号点作为原树的根。

      这样直接消灭掉了情况二。对于情况一,也可以稍微改一改。因为原树的根与已有的树的根重合,可以直接树差分加上树状数组计算出距离。

    Code

     1 /**
     2  * Codeforces
     3  * Problem#980E
     4  * Accepted
     5  * Time: 888ms
     6  * Memory: 98300k
     7  */
     8 #include <bits/stdc++.h>
     9 using namespace std;
    10 typedef bool boolean;
    11 
    12 typedef class IndexedTree {
    13     public:
    14         int *ar;
    15         int s;
    16 
    17         IndexedTree():ar(NULL) {    }
    18         IndexedTree(int s):s(s) {
    19             ar = new int[(s + 1)];
    20             memset(ar, 0, sizeof(int) * (s + 1));
    21         }
    22 
    23         void add(int idx, int val) {
    24             for ( ; idx <= s; idx += (idx & (-idx)))
    25                 ar[idx] += val;
    26         }
    27 
    28         int getSum(int idx) {
    29             int rt = 0;
    30             for ( ; idx; idx -= (idx & (-idx)))
    31                 rt += ar[idx];
    32             return rt;
    33         }
    34 
    35         void add(int l, int r, int val) {
    36             add(l, val), add(r + 1, -val);
    37         }
    38 }IndexedTree;
    39 
    40 int n, m;
    41 int cnt;
    42 vector<int> *g;
    43 int *in, *out;
    44 int *dep, *fas;
    45 boolean *vis;
    46 IndexedTree it;
    47 
    48 inline void init() {
    49     scanf("%d%d", &n, &m);
    50     m = n - m;
    51     g = new vector<int>[(n + 1)];
    52     in = new int[(n + 1)];
    53     out = new int[(n + 1)];
    54     dep = new int[(n + 1)];
    55     fas = new int[(n + 1)];
    56     vis = new boolean[(n + 1)];
    57     it = IndexedTree(n);
    58     memset(vis, false, sizeof(boolean) * (n + 1));
    59     for (int i = 1, u, v; i < n; i++) {
    60         scanf("%d%d", &u, &v);
    61         g[u].push_back(v);
    62         g[v].push_back(u);
    63     }
    64 }
    65 
    66 void dfs(int p, int fa) {
    67     in[p] = ++cnt, dep[p] = dep[fa] + 1, fas[p] = fa;
    68     for (int i = 0; i < (signed)g[p].size(); i++) {
    69         int e = g[p][i];
    70         if (e == fa)    continue;
    71         dfs(e, p);
    72     }
    73     out[p] = cnt;
    74 }
    75 
    76 inline void solve() {
    77     dep[0] = 0;
    78     dfs(n, 0);
    79     vis[n] = true, it.add(in[n], out[n], 1), m--;
    80     for (int i = n - 1; i; i--) {
    81         if (vis[i])    continue;
    82         int added = dep[i] - it.getSum(in[i]);
    83         if (added > m)    continue;
    84         for (int j = i; !vis[j]; j = fas[j])
    85             it.add(in[j], out[j], 1), vis[j] = true, m--;
    86     }
    87     for (int i = 1; i <= n; i++)
    88         if (!vis[i])
    89             printf("%d ", i);
    90 }
    91 
    92 int main() {
    93     init();
    94     solve();
    95     return 0;
    96 }

      

  • 相关阅读:
    高盛、沃尔玛 题做出来还挂了的吐槽
    amazon师兄debrief
    到所有人家距离之和最短的中点 296. Best Meeting Point
    问问题没人回答的情况怎么办终于有解了
    找名人 277. Find the Celebrity
    数组生存游戏 289. Game of Life
    547. Number of Provinces 省份数量
    428. Serialize and Deserialize Nary Tree 序列化、反序列化n叉树
    alias别名简介和使用
    面试官:线程池执行过程中遇到异常会发生什么,怎样处理? Vincent
  • 原文地址:https://www.cnblogs.com/yyf0309/p/9022044.html
Copyright © 2011-2022 走看看