zoukankan      html  css  js  c++  java
  • 【MdOI2020】Path

    QwQ 原定验题人来发个题解。因为我太菜跑路了所以验题人就变成Kubic了。
    一句话题意:选两条不相交的路径,使其边权的异或和的和最大。
    首先分析一下两条路径不相交的性质。
    设路径1的两个端点是(a,b),它们的( ext{lca})(p)。路径2的两个端点是(c,d),它们的( ext{lca})(q)
    情况1:当(c)在子树(p)内部而(d)在子树(p)外部时,(c ightarrow d)一定与(a ightarrow b)相交。
    情况2:当(c)(d)都在子树(p)外部时,(c ightarrow d)一定不与(a ightarrow b)相交。
    (c,d)都在子树(p)内部时,我们可以反过来讨论(a ightarrow b)(q)的关系。
    情况3:当(a)在子树(q)内部而(b)在子树(q)外部时,同情况1。
    情况4:当(a)(b)都在子树(q)外部时,同情况2。
    情况5:当(a)(b)都在子树(q)内部时,(p)(q)必交与一点,故这2条路径一定相交。
    所以如果2条路径不相交则路径1一定在某一子树内,路径2一定在这个子树外
    接着40分做法就一目了然了:枚举一个点,然后枚举在这个子树内的点和这个子树外的点,使这两部分的异或和最大。可以用(01 ext{trie})做到(mathcal{O}(n^2log { max w_i}))
    结合一下链的部分分可以获得(60)分的好成绩。
    接下来才是这题的核心,从(60)分到正解的过度。
    考虑把问题放在序列上,设(L(u))表示子树(u)的开始( ext{dfs})序,(R(u))表示子树(u)的结束( ext{dfs})序。
    则子树(u)对应的询问就是在([L(u),R(u)])选出2个异或和最大的数,在([1,L(u)-1] cup [R(u)+1,n])中选出2个异或和最大的数。
    发现这2个区间并不连续,比较麻烦。但是因为这2个区间的并相当于从整个序列中挖走了一段出来,所以把这个序列复制一遍加到末尾一定可以得到一个完整的连续的覆盖所有信息的区间。
    到了这里其实这道题就结束了,我们相当于对于每个点创造出2个询问,这个点的答案就是这2个询问对应的区间的最大异或和之和。
    可以用回滚莫队+( ext{01trie})做到(mathcal{O}(nsqrt{n}log { max w_i }))
    代码:

    #include <bits/stdc++.h>
    
    const int N = 6e4 + 10;
    int n, m, i, j, k, tot, totq, p = 1, trie_tot = 1, SZ, Ans;
    int dfn[N], siz[N], bel[N], dis[N];
    int fir[N], we[N << 1], to[N << 1], nxt[N << 1]; 
    int ch[N * 26][2], tag[N * 26], ans[N];
    struct ques {
      int l, r, id;
      ques() { l = r = id = 0; }
      ques(int _l, int _r, int i) {
        l = _l, r = _r, id = i; 
      }
      inline friend bool operator < (ques a, ques b) {
        if (bel[a.l] == bel[b.l]) return a.r < b.r;
        else return bel[a.l] < bel[b.l];
      }
    } q[N << 1];  
    inline void adde(int u, int v, int w) {
      static int ce = 0;
      to[++ce] = v, we[ce] = w, nxt[ce] = fir[u], fir[u] = ce;
    }
    inline void insert(int a) {
      int u = 1;
      for (int i = 30; i >= 0; i--) {
        bool v = (a >> i) & 1;
        if (!ch[u][v]) ch[u][v] = ++trie_tot;
        u = ch[u][v];
        tag[u]++;
      }
    }
    inline int query(int a) {
      int u = 1, res = 0;
      for (int i = 30; i >= 0; i--) {
        bool v = (a >> i) & 1;
        if (tag[ch[u][v ^ 1]]) res |= 1 << i, u = ch[u][v ^ 1];
        else u = ch[u][v]; 
      }
      return res;
    }
    inline void del(int a) {
      int u = 1;
      for (int i = 30; i >= 0; i--) {
        bool v = (a >> i) & 1;
        u = ch[u][v];
        tag[u]--;
      }
    }
    void dfs(int u, int par, int w) {
      siz[u] = 1, dfn[u] = ++tot, dis[dfn[u]] = dis[dfn[par]] ^ w;
      for (int i = fir[u], v; i; i = nxt[i]) 
        if ((v = to[i]) != par) {
          int w = we[i];
          dfs(v, u, w);
          siz[u] += siz[v];
        }
    }
    inline int calc(int l, int r) {
      int res = 0;
      for (int i = l; i <= r; i++) res = std::max(res, query(dis[i])), insert(dis[i]); 
      for (int i = l; i <= r; i++) del(dis[i]);
      return res;
    }
    inline void solve(int x) {
      int br = std::min(x * SZ, n), l = br + 1, r = br;
      int _ans = 0;
      for (; bel[q[p].l] == x; p++) {
        int ql = q[p].l, qr = q[p].r;  
        if (bel[qr] == x) { ans[q[p].id] += calc(ql, qr); continue; } 
        while (r < qr) _ans = std::max(_ans, query(dis[++r])), insert(dis[r]);
        int rev = _ans;
        while (l > ql) _ans = std::max(_ans, query(dis[--l])), insert(dis[l]);  
        ans[q[p].id] += _ans;
        while (l <= br) del(dis[l++]); _ans = rev;
      }
      while (r > br) del(dis[r--]);
    }
    
    int main() {
      scanf("%d", &n);
      for (int i = 1, u, v, w; i < n; i++) scanf("%d %d %d",&u, &v, &w), adde(u, v, w), adde(v, u, w); 
      dfs(1, 0, 0); 
      for (int i = 2; i <= n; i++) q[++totq] = ques(dfn[i], dfn[i] + siz[i] - 1, i), q[++totq] = ques(dfn[i] + siz[i], dfn[i] + n - 1, i);
      for (int i = 1; i <= n; i++) dis[i + n] = dis[i];
      n <<= 1; SZ = sqrt(n);
      for (int i = 1; i <= n; i++) bel[i] = (i - 1) / SZ + 1;
      std::sort(q + 1, q + totq + 1);
      for (int i = 1; i <= bel[n]; i++) solve(i);
      for (int i = 1; i <= n; i++) Ans = std::max(Ans, ans[i]);
      printf("%d
    ", Ans);
      return 0;
    }
    
  • 相关阅读:
    leetcode 18 4Sum
    leetcode 71 Simplify Path
    leetcode 10 Regular Expression Matching
    leetcode 30 Substring with Concatenation of All Words
    leetcode 355 Design Twitte
    leetcode LRU Cache
    leetcode 3Sum
    leetcode Letter Combinations of a Phone Number
    leetcode Remove Nth Node From End of List
    leetcode Valid Parentheses
  • 原文地址:https://www.cnblogs.com/Sai0511/p/12389713.html
Copyright © 2011-2022 走看看