zoukankan      html  css  js  c++  java
  • 极简单数据结构乱写

    luogu P3979 遥远的国度

    链覆盖,询问子树最小值, 换根。

    lct 即可。

    可以使用树剖, 询问的时候分类讨论。

    1. id 和 root 相等, 直接询问即可。
    2. root ≠ lca(id, root) ≠ id, 直接询问即可
    3. id 是 root 的祖先, 相当于询问 id 除 root 所在的子树之外的位置, 在 dfs 序上分解即可
    4. root 是 id 的祖先, 直接询问即可
    #include<bits/stdc++.h>
    
    using namespace std;
    
    const int N = 1e5 + 3, inf = 2147483647;
    
    int root;
    int n, m, val[N], a[N];
    int ecnt, hd[N], nt[N*2+1], vr[N*2+1];
    void add(int x, int y) {nt[++ecnt]=hd[x],hd[x]=ecnt; vr[ecnt]=y;}
    
    int dep[N], siz[N], fa[N], tp[N], son[N];
    void dfs1(int x, int F, int D) {
      dep[x] = D, siz[x] = 1, fa[x] = F;
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(y == F) continue;
        dfs1(y, x, D + 1);
        siz[x] += siz[y];
        if(siz[y] > siz[son[x]]) son[x] = y;
      }
    }
    int dfntot, in[N], out[N];
    void dfs2(int x, int T) {
      in[x] = ++dfntot; a[in[x]] = val[x];
      tp[x] = T;
      if(son[x]) dfs2(son[x], T);
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
      out[x] = dfntot;
    }
    
    int up(int x, int D, int Top) {
      while(dep[tp[x]] > D) x = fa[tp[x]];
      return dep[tp[x]] == D ? tp[x] : son[Top];
    }
    
    int t[N<<2], tag[N<<2];
    #define li (me << 1)
    #define ri (li | 1)
    #define mid ((l+r)>>1)
    #define ls li,l,mid
    #define rs ri,mid+1,r
    void build(int me, int l, int r) {
      if(l == r) { t[me] = a[l]; return; }
      build(ls), build(rs), t[me] = min(t[li], t[ri]);
    }
    void ad(int me, int v) { t[me] = tag[me] = v;}
    void pushdown(int me) {
      if(tag[me]) {ad(li, tag[me]), ad(ri, tag[me]), tag[me] = 0; }
    }
    void modi(int me, int l, int r, int x, int y, int v) {
      if(x <= l && r <= y) { ad(me, v); return;}
      pushdown(me);
      if(x<=mid) modi(ls, x, y, v);
      if(y >mid) modi(rs, x, y, v);
      t[me] = min(t[li], t[ri]);
    }
    int ask(int me, int l, int r, int x, int y) {
      if(x <= l && r <= y) return t[me];
      pushdown(me);
      int ret = inf;
      if(x<=mid) ret = ask(ls, x, y);
      if(y >mid) ret = min(ret, ask(rs, x, y));
      return ret;
    }
    void road_modi(int x, int y, int v) {
      while(tp[x] != tp[y]) {
        if(dep[tp[y]] > dep[tp[x]]) swap(x, y);
        modi(1, 1, n, in[tp[x]], in[x], v);
        x = fa[tp[x]];
      }
      if(dep[y] > dep[x]) swap(x, y);
      modi(1, 1, n, in[y], in[x], v);
    }
    
    int main()
    {
      scanf("%d%d", &n, &m);
      for(int i = 1; i < n; ++i) {
        int x, y; scanf("%d%d", &x, &y); add(x, y), add(y, x);
      }
      for(int i = 1; i <= n; ++i) scanf("%d", &val[i]);
      scanf("%d", &root);
      dfs1(1, 0, 1), dfs2(1, 1);
      
      build(1, 1, n);
      
      int opt, id, x, y, v;
      while(m--)
      {
        scanf("%d", &opt);
        if(opt == 1)
        {
          scanf("%d", &id);
          root = id;
        }
        else
        if(opt == 2)
        {
          scanf("%d%d%d", &x, &y, &v);
          road_modi(x, y, v);
        }
        else
        if(opt == 3)
        {
          scanf("%d", &id);
          if(id == root) cout << t[1] << '
    ';
          else
          {
            if(in[id] <= in[root] && out[root] <= out[id])
            {
              int tmp = up(root, dep[id] + 1, id);
              int l = in[tmp] - 1, r = out[tmp] + 1;
              int ret = inf;
              if(1 <= l) ret = ask(1, 1, n, 1, l);
              if(r <= n) ret = min(ret, ask(1, 1, n, r, n));
              cout << ret << '
    ';
            }
            else
              cout << ask(1, 1, n, in[id], out[id]) << '
    ';
          }
        }
      }
      return 0;
    }
    

    对于一颗 n 个点的树, 给定 m 个关键点, 可以构造一棵点数为 O(m) 的保持原树祖先关系的树, 称为虚树。为了方便,通常会把原树的根默认为关键点。

    预处理出原树的 dfs 序, 这样,按这个 dfs 序访问每组关键点, 相当于在虚树上 dfs, dfs 的过程中连边并添加辅助点即可。

    Luogu P2495 [SDOI2011] 消耗战

    设 dp[u] 为割掉 u 子树内所有关键点的最小代价, 若 u 为关键点, 则 dp[u] 为 u 到根的最小边权,反之, dp[u] = min(u 到根的最小边权,Σv∈u.son dp[v])。

    发现 dp 子树内没有关键点的点是没有意义的, 进而, 对于子树内关键点集合相同的若干点, 有很多都是无用的, 于是在虚树上 DP, 答案不变。

    // 这段代码是优化过的, 不是原始的虚树 DP。具体地, 建出的虚树的关键点都不记录其子树。
    #include<bits/stdc++.h>
    typedef long long LL;
    using namespace std;
    int read() {
      int x = 0; char c = getchar();
      while(c < '0' || c > '9') c = getchar();
      while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
      return x;
    }
    const int N = 250003;
    const LL inf = 1e18;
    
    int n;
    int ecnt, hd[N], nt[N*2+1], vr[N*2+1], w[N*2+1];
    void ad(int u, int v, int w_) {nt[++ecnt] = hd[u], hd[u] = ecnt, vr[ecnt] = v, w[ecnt] = w_; }
    
    LL mi[N];
    int dep[N], siz[N], fa[N], son[N], tp[N];
    void dfs1(int x, int F, int D) {
      dep[x] = D, siz[x] = 1, fa[x] = F;
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(y == F) continue;
        mi[y] = min(mi[x], (LL)w[i]);
        dfs1(y, x, D + 1);
        siz[x] += siz[y];
        if(siz[y] > siz[son[x]]) son[x] = y;
      }
    }
    int dfntot, dfn[N];
    void dfs2(int x, int T) {
      dfn[x] = ++dfntot;
      tp[x] = T;
      if(son[x]) dfs2(son[x], T);
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
      }
    }
    int lca(int x, int y) {
      while(tp[x] != tp[y]) dep[tp[x]] > dep[tp[y]] ? x = fa[tp[x]] : y = fa[tp[y]];
      return dep[x] > dep[y] ? y : x;
    }
    
    
    int k, h[N];
    bool cmp(int s1, int s2) { return dfn[s1] < dfn[s2]; }
    vector<int> v[N];
    
    int s[N], t;
    void extnd(int x) {
      if(t == 1) { s[++t] = x; return; }
      int l = lca(s[t], x);
      if(l == s[t]) {  return; }
      while(t > 1 && dfn[s[t - 1]] >= dfn[l]) v[s[t - 1]].push_back(s[t]), --t;
      if(l != s[t]) v[l].push_back(s[t]), s[t] = l;
      s[++t] = x;
    }
    
    LL dp(int x) {
      if(v[x].size() == 0) return mi[x];
      LL tmp = 0ll;
      for(int i = 0; i < (int)v[x].size(); ++i) tmp += dp(v[x][i]);
      v[x].clear();
      return min(mi[x], tmp);
    }
    
    int main()
    {
      n = read();
      for(int i = 1; i < n; ++i) {
        int x = read(), y = read(), z = read(); ad(x, y, z), ad(y, x, z);
      }
      mi[1] = inf;
      dfs1(1, 0, 1), dfs2(1, 1);
      
      int m = read();
      while(m--)
      {
        k = read();
        for(int i = 1; i <= k; ++i) h[i] = read();
        sort(h + 1, h + 1 + k, cmp);
        s[t = 1] = 1;
        for(int i = 1; i <= k; ++i) extnd(h[i]);
        while(t > 0) v[s[t - 1]].push_back(s[t]), --t;
        printf("%lld
    ", dp(1));
      }
      return 0;
    }
    

    定理:对于任何高度为 O(log n) 的树, Σu siz[u] = O(n log n)。

    点分治时, 用当前层的中心当作下一层所有中心的父亲, 就可以得到一棵点分治重构树。

    可根据上面的定理可以证明点分治的复杂度。

    梳理下淀粉质板子。

    //淀粉质的主过程,即构建淀粉树的过程
    void calc(int sta) {
        //第一步得到当前淀粉树子树的 size
        get_mid(sta, 0);
        //而后找出重心
        sumsize = siz[sta];
        get_mid(sta, 0);
        vis[mid] = true;
        
        //从这开始计算分治中心
        
        //从这结束计算分治中心
        
        //分治
        for(int i = hd[x]; i; i = nt[i]) {
            int y = vr[i];
            if(vis[y]) continue;
            calc(y);
        }
    }
    

    Luogu P4178 Tree

    可以直接点分治, 像 DSU on Tree 那样用 dfn 会爽许多。

    由于 k 较小, 统计的时候可以用数据结构搞, 而不必用双指针+容斥。

    我会将两种写法都写一遍。

    // 数据结构统计, 缺点是难以应付 k 较大的情况
    #include<bits/stdc++.h>
    typedef long long LL;
    using namespace std;
    
    const int N = 4e4 + 233, K = 2e4 + 233;
    
    int n, k;
    int ecnt, hd[N], nt[N*2+1], vr[N*2+1], we[N*2+1];
    void ad(int u, int v, int w) {nt[++ecnt] = hd[u], hd[u] = ecnt; vr[ecnt] = v, we[ecnt] = w; }
    
    LL ans = 0ll;
    
    bool vis[N];
    int siz[N];
    int sumsiz, mid;
    void get_mid(int x, int F) { int mx = 0;
      siz[x] = 1;
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(y == F || vis[y]) continue;
        get_mid(y, x);
        siz[x] += siz[y];
        mx = max(mx, siz[y]);
      }
      mx = max(mx, sumsiz - siz[x]);
      if(mx <= (sumsiz / 2)) mid = x;
    }
    
    int dis[N];
    int dfntot, in[N], out[N], row[N];
    void dfs(int x, int F) {
      in[x] = ++dfntot; row[dfntot] = x;
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(y == F || vis[y]) continue;
        dis[y] = dis[x] + we[i];
        dfs(y, x);
      }
      out[x] = dfntot;
    }
    
    int t[K];
    void ins(int x) {
      if(!x) return;
      for(; x <= k; x += (x & (-x))) ++t[x];
    }
    int ask(int x) {
      int res = 0;
      for(; x; x -= (x & (-x))) res += t[x];
      return res;
    }
    
    void fuck(int x) {
      dfntot = 0;
      dis[x] = 0;
      dfs(x, 0);
      memset(t, 0, sizeof t);
      
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(vis[y]) continue;
        for(int j = in[y]; j <= out[y]; ++j) {
          int Y = row[j];
          if(dis[Y] <= k) ++ans;
          if(dis[Y] < k) ans += ask(k - dis[Y]);
        }
        for(int j = in[y]; j <= out[y]; ++j) {
          int Y = row[j];
          if(dis[Y] < k) ins(dis[Y]);
        }
      }
    }
    
    void calc(int sta) {
      get_mid(sta, 0);
      sumsiz = siz[sta];
      get_mid(sta, 0);
      vis[mid] = true;
      
      if(siz[mid] > 1) fuck(mid);
      
      for(int i = hd[mid]; i; i = nt[i]) {
        int y = vr[i];
        if(vis[y]) continue;
        calc(y);
      }
    }
    
    int main()
    {
      scanf("%d", &n);
      for(int i = 1; i < n; ++i) {
        int u, v, w; scanf("%d%d%d", &u, &v, &w); ad(u, v, w), ad(v, u, w);
      }
      scanf("%d", &k);
      calc(1);
      cout << ans;
      return 0;
    }
    
    // 双指针+容斥, 有复杂度优势, 常数不足为惧
    #include <bits/stdc++.h>
    typedef long long LL;
    using namespace std;
    
    const int N = 4e4 + 23;
    
    int n, k;
    int ecnt, hd[N], nt[N*2+1], vr[N*2+1], we[N*2+1];
    void ad(int u, int v, int w) { nt[++ecnt] = hd[u], hd[u] = ecnt; vr[ecnt] = v, we[ecnt] = w; }
    
    LL ans = 0ll;
    bool vis[N];
    
    int siz[N];
    int sumsiz, mid;
    void get_mid(int x, int F) {int mx = 0;
      siz[x] = 1;
      for(int i = hd[x]; i; i = nt[i]) {
        int y =  vr[i];
        if(y == F || vis[y]) continue;
        get_mid(y, x);
        siz[x] += siz[y];
        mx = max(mx, siz[y]);
      }
      mx = max(mx, sumsiz - siz[x]);
      if(mx <= (sumsiz / 2)) mid = x;
    }
    
    int dis[N];
    int dfntot, in[N], out[N], row[N];
    void dfs(int x, int F) {
      in[x] = ++dfntot; row[dfntot] = x;
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(y == F || vis[y]) continue;
        dis[y] = dis[x] + we[i];
        dfs(y, x);
      }
      out[x] = dfntot;
    }
    
    int s[N], t;
    
    LL gao() {
      LL res = 0ll;
      sort(s+1, s+1+t);
      int l = 1, r = t;
      while(l < r && l <= t) {
        while(r >= 1 && s[r] + s[l] > k) --r;
        if(l < r) res += (r - l);
        ++l;
      }
      return res;
    }
    
    void fuck(int x) {
      dfntot = 0;
      dis[x] = 0;
      dfs(x, 0);
      
      t = 0;
      for(int i = in[x]; i <= out[x]; ++i) s[++t] = dis[row[i]];
      ans += gao();
      for(int i = hd[x]; i; i = nt[i]) {
        int y = vr[i];
        if(vis[y]) continue;
        
        t = 0;
        for(int j = in[y]; j <= out[y]; ++j) {
          s[++t] = dis[row[j]];
        }
        ans -= gao();
      }
    }
    
    void calc(int sta) {
      get_mid(sta, 0);
      sumsiz = siz[sta];
      get_mid(sta, 0);
      vis[mid] = true;
      
      if(siz[mid] > 1) fuck(mid);
      
      for(int i = hd[mid]; i; i = nt[i]) {
        int y = vr[i];
        if(vis[y]) continue;
        calc(y);
      }
    }
    
    int main() {
      
      scanf("%d", &n);
      for(int i = 1; i < n; ++i) {
        int u, v, w; scanf("%d%d%d", &u, &v, &w); ad(u, v, w), ad(v, u, w);
      }
      scanf("%d", &k);
      calc(1);
      cout << ans;
      return 0;
    }
    
  • 相关阅读:
    HDU 1124 Factorial
    hdu 1690 Bus System
    hdu 1113 Word Amalgamation
    POJ 2482 Stars in Your Window
    hdu 1385 ZOJ 1456 Minimum Transport Cost(经典floyd)
    hdu 1907 John
    VMware 虚拟机 安装 UBuntu 9.10 命令模式转换成窗口模试
    #pragma CODE_SEG __NEAR_SEG NON_BANKED详解
    Ubuntu 下Hadoop 伪分布式 hadoop0.20.2.tar.gz 的安装
    文件拷贝代码以及疑问
  • 原文地址:https://www.cnblogs.com/tztqwq/p/14361951.html
Copyright © 2011-2022 走看看