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,n\leq 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 ? '\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, '\n', 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 \times poly(\log))\) 的做法,但是沒調出來。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章