zoukankan      html  css  js  c++  java
  • 10月15日模拟赛题解

    10月15日模拟赛题解

    A 树

    Description

    给定一棵 (n) 个节点的树,每个节点有两个参数 (a,~b),对于每个节点,求子树中参数为 (b) 的所有节点的 (a) 之和

    Limitations

    (100\%) (1 leq b leq n leq 10^5,~a leq 1000)

    (60\%) (1 leq b,nleq 1000)

    (30\%) (1 leq b, n leq 10)

    Solution

    对于 (30\%) 的数据,我也不会做。

    对于 (60\%) 的数据,考虑枚举一下 (b),然后 (O(n)) 在树上 dfs 即可。

    考虑满分做法。

    一个显而易见的想法是,对于一个 (b_i),我们遍历他子树中参数 (b)(b_i) 不同的节点是没有意义的,因此我们考虑遍历完一个节点以后直接跳到它子树内最靠近它的 (b) 相同的节点,换句话说,对每个节点,寻找一个与它参数 (b) 相同的最近的祖先作为父节点。

    考虑在 dfs 的时候,如果我们对每个 (b) 维护根到当且节点的链上参数为 (b) 的节点序列,则当前节点的同参数父节点就是对应序列的最后一个节点。考虑维护这个节点序列只需要开 (n) 个栈分别维护每个 (b) 的序列即可,即 dfs 到一个节点时将节点入栈,离开时弹出,这样显然可以保证栈内元素是有序的根到当前节点链上的节点序列。

    然后我们相当于对于每个 (b) 都新建了一棵树,在这些树上做 dfs 即可。事实上在原来的树上做 dfs 的时候只要维护好每个节点在新树上的孩子就可以直接统计,不需要显式地建出新树。

    时空复杂度 (O(n)),期望得分 (100~pts)

    Code

    #include <cstdio>
    #include <stack>
    #include <vector>
    
    const int maxn = 100010;
    
    struct Edge {
      int v;
      Edge *nxt;
    
      Edge(const int _v, Edge *h) : v(_v), nxt(h) {}
    };
    Edge *hd[maxn];
    
    int n;
    int b[maxn], ans[maxn];
    bool vis[maxn];
    std::stack<int>stk[maxn];
    std::vector<int>son[maxn];
    
    void dfs(const int u);
    
    int main() {
      freopen("tree.in", "r", stdin);
      freopen("tree.out", "w", stdout);
      qr(n);
      for (int i = 1; i <= n; ++i) {
        qr(ans[i]); qr(b[i]);
      }
      for (int i = 1, u, v; i < n; ++i) {
        u = v = 0; qr(u); qr(v);
        hd[u] = new Edge(v, hd[u]);
        hd[v] = new Edge(u, hd[v]);
      }
      dfs(1);
      for (int i = 1; i <= n; ++i) {
        qw(ans[i], i == n ? '
    ' : ' ', true);
      }
      return 0;
    }
    
    void dfs(const int u) {
      vis[u] = true;
      int k = b[u];
      if (stk[k].size()) {
        son[stk[k].top()].push_back(u);
      }
      stk[k].push(u);
      for (auto e = hd[u]; e; e = e->nxt) if (!vis[e->v]) {
        int v = e->v;
        dfs(v);
      }
      for (auto v : son[u]) {
        ans[u] += ans[v];
      }
      stk[k].pop();
    }
    

    B 小偷

    Description

    有一个长度不低于 (4) 的序列 (A),满足 (frac{A_i}{A_{i - 1}} = k),其中 (k) 是一个正整常数,且序列的最大值不超过 (n)

    显然这样的合法序列有许多个,现在我们对序列的参数一无所知,只知道合法的序列有 (m) 个,求 (n) 最小是多少。无解输出 -1

    Limitations

    (100\%) (3 leq m leq 10^{15})

    (60\%) (3 leq m leq 100000)

    (30\%) (3 leq m leq 1000)

    Solution

    对于前 (30\%) 的数据,爆搜一下序列即可。

    对于前 (60\%) 的数据,枚举一下 (k) 即可,然后枚举序列长度即可。值得注意的是由于序列每次除以 (k),因此序列长度是 (O(log)) 级别的,并且序列最大值大概是 (O(m)) 级别,最大值至少除 (3)(k) 后的结果必须是正数,因此 (k) 也不会很大,大概在 (10^2) 数量级,因此枚举可过。时间复杂度 (O(m log m))

    至于证明最大值是 (O(m)) 的……我是打个表看出来的(雾

    对于全部的数据,考虑求最小值,并且显然不可能贪心,DP由于序列长度不确定也无法设计状态,因此考虑二分答案。

    考虑固定一个 (n),当 (n) 变大时,合法的序列个数不会变小,因为 (n) 较小时合法的序列,当 (n) 变大后依然是合法的。可以据此二分答案。

    二分答案后,考虑枚举首相 (k),然后枚举序列长度即可。同样由于 (n)(O(m)) 的,得到 (k leq 10^frac{15}{4} leq 10^4)。又因为序列长度是 (O(log m)) 的,因此求方案数的总复杂度是 (O(log m 10^frac{log m}{4}))

    因此总的时间复杂度是 (O(10^{frac{log m}{4}} log^2 m))

    #include <cstdio>
    
    typedef long long int ll;
    
    ll m, ans;
    
    ll calc(const ll n);
    
    int main() {
      freopen("thief.in", "r", stdin);
      freopen("thief.out", "w", stdout);
      qr(m);
      for (ll l = 1, r = 1000000000000000000ll, mid = 1000000000ll; l <= r; mid = (l + r) >> 1) {
        if (calc(mid) >= m) {
          ans = mid; r = mid - 1;
        } else {
          l = mid + 1;
        }
      }
      qw(calc(ans) == m ? ans : -1, '
    ', true);
      return 0;
    }
    
    ll calc(const ll n) {
      ll _ret = 0;
      for (int k = 2; ; ++k) {
        ll cnt = 0;
        for (ll i = n; i; i /= k) {
          if ((++cnt) >= 4) { _ret += i; }
        }
        if (cnt < 4) break;
      }
      return _ret;
    }
    

    C 盘子

    Description

    给定 (n) 个圆,第 (i) 个圆的半径为 (r_i),又有 (m) 个间距为 (1) 且排成一排的点,要求以这些点作为这 (n) 个圆的圆心,且圆之间两两不相交但允许相切,求方案数对大质数取模的结果。

    Limitations

    (100\%) (3 leq n leq 4000,~m leq 10^9, r_i leq 10^5)

    (60\%) (3 leq n leq 100,~~m leq 200)

    (30\%) (2 leq n leq 5,~~m leq 30)

    Solution

    (30\%) 爆搜即可。时间复杂度 (C_{m}^{n})

    对于 (60\%) 的数据,考虑只考虑圆心所在的直线,则问题转化为了给定一堆线段,求互不相交的安放方案数。

    考虑把一段长度为 (1) 的空白也看做一个元素,则问题转化为了有重复元素的全排列数,套公式即可。

    注意到最两边的线段是可以有至多一半露在外面的,考虑枚举这两端的元素,然后枚举他们有多少露在外面,暴力算每种情况的方案数公式,注意到露在外面的每向里 (1) 的长度时,这种情况的方案数可以 (O(1)) 维护,这样就得到了一个 (O(n^3 + n^2 r)) 的做法,然后注意到两侧线段向里移动的过程中,每个数字在公式中产生的贡献是可以 (O(1)) 计算的,因此得到了一个 (O(n^3 + n^2 imes poly(log))) 的做法,但是没调出来。

  • 相关阅读:
    Ubuntu 中软件的安装、卸载以及查看的方法总结
    无锁队列的环形数组实现
    Geodatabase模型
    地图投影与ArcGIS坐标系转换
    ARCGIS动态画点
    ArcEngine数据删除几种方法和性能比较
    AE开发技术文档--8种数据访问方法
    Hibernate 的HQL和sql有什么区别
    ActionContextCleanUp作用
    Hibernate延迟加载与opensessioninviewFilter
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/11682102.html
Copyright © 2011-2022 走看看