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 }

      

  • 相关阅读:
    12-Factor与云原生Part2
    Openshift部署流程介绍
    软件开发过程中的环境简介
    基于Openshift的SpringBoot微服务
    日志审计系统设计
    dotnetcore执行shell脚本
    12-Factor与云原生
    项目管理思考小记之一
    DevOps工程师的成长路线图
    消费者驱动的契约Consumer drivern Contract
  • 原文地址:https://www.cnblogs.com/yyf0309/p/9022044.html
Copyright © 2011-2022 走看看