【Code Chef】April Challenge 2019

Subtree Removal

很显然不可能选择砍掉一对有祖先关系的子树。令$f_i$表示$i$子树的答案,如果$i$不被砍,那就是$a_i + \sum\limits_j f_j$;如果$i$被砍,那就是$-x$。取个$max$就好了。

时间、空间复杂度$O(n)$。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

int tc, n, xx;
int a[N];
vector<int> g[N];
long long f[N];

void Dfs(int x, int ft) {
  f[x] = a[x];
  for (int i = 0; i < g[x].size(); ++i) {
    int v = g[x][i];
    if (v == ft) continue;
    Dfs(v, x);
    f[x] += f[v];
  }
  f[x] = max(f[x], -(long long)xx);
}

int main() {
  scanf("%d", &tc);
  for (; tc--; ) {
    scanf("%d%d", &n, &xx);
    for (int i = 1; i <= n; ++i) {
      scanf("%d", &a[i]);
    }
    for (int i = 1, x, y; i < n; ++i) {
      scanf("%d%d", &x, &y);
      g[x].push_back(y);
      g[y].push_back(x);
    }

    Dfs(1, 0);
    printf("%lld\n", f[1]);
    
    // remember to clear up
    for (int i = 1; i <= n; ++i) {
      g[i].clear();
    }
  }
  
  return 0;
}
View Code

 

Playing with Numbers

在模$m$意义下,$a * k(k \in \mathbb{N})$能表示的最大的数就是$m - (a, m)$。容易推导出一个叶子的答案就是$m_i - (m, a_{b_1}, a_{b_2}, ... , a_{b_w})$,其中$b$表示$i$号点的祖先链。

时间复杂度$O(nlogn)$,空间复杂度$O(n)$。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

int tc, n;
vector<int> g[N];
long long a[N], m[N], gcd[N];

void Dfs(int x, int ft) {
  for (int i = 0; i < g[x].size(); ++i) {
    int v = g[x][i];
    if (v == ft) continue;
    gcd[v] = __gcd(gcd[x], a[v]);
    Dfs(v, x);
  }
}

int main() {
  scanf("%d", &tc);
  for (; tc--; ) {
    scanf("%d", &n);
    for (int i = 1, x, y; i < n; ++i) {
      scanf("%d%d", &x, &y);
      g[x].push_back(y);
      g[y].push_back(x);
    }
    for (int i = 1; i <= n; ++i) {
      scanf("%lld", &a[i]);
    }
    for (int i = 1; i <= n; ++i) {
      scanf("%lld", &m[i]);
    }
    gcd[1] = a[1];
    Dfs(1, 0);

    for (int i = 2; i <= n; ++i) {
      if (g[i].size() == 1) {
        long long d = __gcd(gcd[i], m[i]);
        printf("%lld ", m[i] - d);
      }
    }
    printf("\n");
    
    // remember to clear up
    for (int i = 1; i <= n; ++i) {
      g[i].clear();
    }
  }
  
  return 0;
}
View Code

 

Kira Loves Palindromes

令$f_{i,j}$表示以$i,j$为端点分别向左右两边扩展最长能匹配的长度。题目要求两个子串$s1, s2$拼起来是回文串的数量,我们假设其回文中心在$s2$里(在$s1$的话反过来在做一遍即可),我们枚举$s2$的左端点的位置$i$,在枚举回文中心的右端点$j$,则此时有方案数$\sum\limits_{k = 1}^{i - 1} f_{k, j + 1}$。对$f$做个前缀和,外面枚举个$i,j$即可。

时间、空间复杂度$O(n^2)$。

#include <bits/stdc++.h>

using namespace std;

typedef unsigned long long ULL;

const int N = 1e3 + 5;
const ULL BAS = 137;

int n;
char s[N];
int f[N][N], s1[N][N], s2[N][N];
long long ans;
ULL H[N], hz[N], hv[N];

ULL Get(int l, int r, ULL *h) {
  return (h[r] - h[l - 1]) * H[N - l];
}

bool Chkp(int l, int r) {
  if (l > r) return 1;
  int d = (r - l + 1) / 2;
  return Get(l, l + d - 1, hz) == Get(n - r + 1, n - r + d, hv);
}

int main() {
  H[0] = 1;
  for (int i = 1; i < N; ++i) {
    H[i] = H[i - 1] * BAS;
  }
  
  scanf("%s", s + 1);
  n = strlen(s + 1);
  for (int i = 1; i <= n; ++i) {
    hz[i] = hz[i - 1] + H[i] * s[i];
    hv[i] = hv[i - 1] + H[i] * s[n - i + 1];
  }

  for (int i = 1; i <= n; ++i) {
    for (int j = n; j > i; --j) {
      if (s[i] == s[j]) {
        f[i][j] = f[i - 1][j + 1] + 1;
      }
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = n; j > i; --j) {
      s1[i][j] = s1[i][j + 1] + f[i][j];
    }
  }
  for (int j = 1; j <= n; ++j) {
    for (int i = 1; i < j; ++i) {
      s2[i][j] = s2[i - 1][j] + f[i][j];
    }
  }
  
  for (int i = 2; i <= n; ++i) {
    for (int j = i; j <= n; ++j) {
      if (!Chkp(i, j - 1)) continue;
      ans += s2[i - 1][j];
    }
  }
  for (int i = 1; i < n; ++i) {
    for (int j = 1; j < i; ++j) {
      if (!Chkp(j + 1, i)) continue;
      ans += s1[j][i + 1];
    }
  }

  printf("%lld\n", ans);
  
  return 0;
}
View Code

 

Offer for Chef

题中的那个运算就是位运算与,然后有用的位置只有$50$个(即如果$k$大于$t$中非$0$个数答案就是$0$)。可以考虑诸位确定,$check$时写个$f_{i,j}$表示把前$i$个数分成$j$段,每段和都是当前要$check$的数的母集即可。

时间复杂度$O(50^4)$,空间复杂度$O(n)$。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;
const int M = 53;

int n, nq, k;
int t[N];
long long a[N];
set<long long> f[M][M];

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i) {
    scanf("%lld", &a[i]);
  }

  scanf("%d", &nq);
  for (; nq--; ) {
    scanf("%d", &k);
    vector<long long> w, sw;
    long long tmp = 0;
    for (int i = 1; i <= n; ++i) {
      scanf("%d", &t[i]);
      if (t[i]) {
        w.push_back(a[i] * t[i]);
        tmp += a[i] * t[i];
        sw.push_back(tmp);
      }
    }
    
    if (k > w.size()) {
      printf("0\n");
      continue;
    }

    for (int i = 0; i < M; ++i) {
      for (int j = 0; j < M; ++j) {
        f[i][j].clear();
      }
    }
    for (int i = 0; i < w.size(); ++i) {
      f[i][1].insert(sw[i]);
    }
    
    for (int i = 0; i < w.size(); ++i) {
      for (int j = 1; j < k; ++j) {
        for (int l = i + 1; l < w.size(); ++l) {
          for (long long e : f[i][j]) {
            f[l][j + 1].insert(e & (sw[l] - sw[i]));
          }
        }
      }
    }
    printf("%lld\n", *f[w.size() - 1][k].rbegin());
  }
  
  return 0;
}
View Code

 

Mininum XOR over Tree

不说了,直接$trie$树合并。

时间、空间复杂度$O(nlogn)$。

#include <bits/stdc++.h>

using namespace std;

const int T = 20;
const int N = 2e5 + 5;
const int M = N * T * 2;

struct Node {
  int ch[2], idm;
} node[M];

int tc, n, m, nq, tot;
int w[N], rt[N];
vector<int> g[N];

int Chkmin(int x, int y) {
  if (!x || !y) return x | y;
  return min(x, y);
}

void Ins(int x) {
  rt[x] = ++tot;
  int p = rt[x];
  for (int i = T - 1; ~i; --i) {
    int c = w[x] >> i & 1;
    if (!node[p].ch[c]) {
      node[p].ch[c] = ++tot;
    }
    p = node[p].ch[c];
  }
  node[p].idm = x;
}

int Merge(int x, int y) {
  if (!x || !y) return x | y;
  int z = ++tot;
  node[z].ch[0] = Merge(node[x].ch[0], node[y].ch[0]);
  node[z].ch[1] = Merge(node[x].ch[1], node[y].ch[1]);
  node[z].idm = Chkmin(node[x].idm, node[y].idm);
  return z;
}

void Dfs(int x, int ft) {
  Ins(x);
  for (int i = 0; i < g[x].size(); ++i) {
    int v = g[x][i];
    if (v == ft) continue;
    Dfs(v, x);
    rt[x] = Merge(rt[x], rt[v]);
  }
}

int main() {
  scanf("%d", &tc);
  for (; tc--; ) {
    scanf("%d%d", &n, &nq);
    for (int i = 1; i <= n; ++i) {
      scanf("%d", &w[i]);
    }
    for (int i = 1, x, y; i < n; ++i) {
      scanf("%d%d", &x, &y);
      g[x].push_back(y);
      g[y].push_back(x);
    }
    Dfs(1, 0);

    int lid = 0, lval = 0;
    for (int x, v; nq--; ) {
      scanf("%d%d", &x, &v);
      x ^= lid;
      v ^= lval;
      int p = rt[x], val = 0;
      for (int i = T - 1; ~i; --i) {
        int c = v >> i & 1;
        if (node[p].ch[c ^ 1]) {
          val += 1 << i;
          p = node[p].ch[c ^ 1];
        } else {
          p = node[p].ch[c];
        }
      }
      lid = node[p].idm;
      lval = val;
      printf("%d %d\n", lid, lval);
    }
    
    // remember to clear up
    for (int i = 1; i <= tot; ++i) {
      node[i].ch[0] = node[i].ch[1] = node[i].idm = 0;
    }
    tot = 0;
    for (int i = 1; i <= n; ++i) {
      g[i].clear();
      rt[i] = 0;
    }
  }
  
  return 0;
}
View Code

 

Edgy

考虑边的联通块个数就是$n - $所有出边颜色都相同的点的个数$+1$。我们只需要维护所有出边颜色都相同的点的个数即可。先轻重链剖分,令$nat_x$表示所有连向虚儿子的边的颜色种类(用$0$表示没有虚儿子,用$1,2$分别表示全都是$0/1$,用$3$表示$0/1$都有),然后每个点最多还剩下两条边(连向父亲的和连向重儿子的),只有这两条边的颜色和$nat$匹配才会算贡献。我们可以在树链上建线段树,维护翻转标记的同时维护区间内有贡献的点的个数。每次修改拆成两条到根的链,在线段树上执行区间翻转即可。注意,翻转轻边时候可能会修改该边上端点的$nat$值。

时间复杂度$O(nlog^2n)$,空间复杂度$O(n)$。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 5;

int tc, n, nq, clk, ans;
int fa[N], fw[N], si[N], so[N], tp[N];
int dfn[N], li[N], nat[N], cnt[N][2];
vector<pair<int, bool> > g[N];

bool Satis(int x, int a, int b) {
  if (a != b) return 0;
  if (!nat[x] || nat[x] == a + 1) return 1;
  return 0;
}

namespace S {
  const int M = N * 4;
  struct Node {
    int zl, zr, zsum, revsum, flp;
  } node[M];

  vector<pair<Node, int> > qry;

  void Up(int t, int md) {
    Node &now = node[t];
    Node &ls = node[t << 1];
    Node &rs = node[t << 1 | 1];
    now.zl = ls.zl;
    now.zr = rs.zr;
    now.zsum = ls.zsum + rs.zsum + Satis(li[md], ls.zr, rs.zl);
    now.revsum = ls.revsum + rs.revsum + Satis(li[md], ls.zr ^ 1, rs.zl ^ 1);
  }

  void Uflp(int t) {
    Node &now = node[t];
    now.zl ^= 1;
    now.zr ^= 1;
    now.flp ^= 1;
    swap(now.zsum, now.revsum);
  }

  void Down(int t) {
    if (node[t].flp) {
      Uflp(t << 1);
      Uflp(t << 1 | 1);
      node[t].flp = 0;
    }
  }
  
  void Build(int t, int l, int r) {
    Node &now = node[t];
    now.zsum = now.revsum = now.flp = 0;
    if (l == r) {
      now.zl = now.zr = fw[li[l]];
      return;
    }
    int md = (l + r) >> 1;
    Build(t << 1, l, md);
    Build(t << 1 | 1, md + 1, r);
    Up(t, md);
  }

  void Flip(int t, int l, int r, int L, int R) {
    if (L <= l && r <= R) {
      Uflp(t);
      return;
    }
    int md = (l + r) >> 1;
    Down(t);
    if (L <= md) Flip(t << 1, l, md, L, R);
    if (R > md) Flip(t << 1 | 1, md + 1, r, L, R);
    Up(t, md);
  }
  
  void Modify(int t, int l, int r, int x) {
    if (l == r) return;
    int md = (l + r) >> 1;
    Down(t);
    if (x < md) Modify(t << 1, l, md, x);
    if (x > md) Modify(t << 1 | 1, md + 1, r, x);
    Up(t, md);
  }

  void Query(int t, int l, int r, int L, int R) {
    if (L <= l && r <= R) {
      qry.push_back(pair<Node, int>(node[t], r));
      return;
    }
    int md = (l + r) >> 1;
    Down(t);
    if (L <= md) Query(t << 1, l, md, L, R);
    if (R > md) Query(t << 1 | 1, md + 1, r, L, R);
  }

  int Ask(int t, int l, int r, int x) {
    if (l == r) return node[t].zl;
    int md = (l + r) >> 1;
    Down(t);
    if (x <= md) return Ask(t << 1, l, md, x);
    return Ask(t << 1 | 1, md + 1, r, x);
  }

  int Query(int L, int R) {
    if (L > R) return 0;
    qry.clear();
    Query(1, 1, n, L, R);
    int ret = 0;
    for (int i = 0; i < qry.size(); ++i) {
      ret += qry[i].first.zsum;
      if (i) {
        ret += Satis(li[qry[i - 1].second], qry[i - 1].first.zr, qry[i].first.zl);
      }
    }
    return ret;
  }
}

bool Chk(int x) {
  if (x == 1) {
    int ws = S::Ask(1, 1, n, dfn[so[x]]);
    return Satis(x, ws, ws);
  }
  if (!so[x]) {
    int w = S::Ask(1, 1, n, dfn[x]);
    return Satis(x, w, w);
  } else {
    int w = S::Ask(1, 1, n, dfn[x]);
    int ws = S::Ask(1, 1, n, dfn[so[x]]);
    return Satis(x, ws, w);
  }
}

void Wish(int x) {
  if (cnt[x][0] && cnt[x][1]) {
    nat[x] = 3;
  } else if (cnt[x][0]) {
    nat[x] = 1;
  } else if (cnt[x][1]) {
    nat[x] = 2;
  }
}

void Dfs0(int x) {
  si[x] = 1;
  for (int i = 0; i < g[x].size(); ++i) {
    int v = g[x][i].first, w = g[x][i].second;
    if (v == fa[x]) continue;
    fa[v] = x;
    fw[v] = w;
    Dfs0(v);
    si[x] += si[v];
    if (si[v] > si[so[x]]) so[x] = v;
  }
}

void Dfs1(int x, int gr) {
  tp[x] = gr;
  dfn[x] = ++clk;
  li[clk] = x;
  if (so[x]) Dfs1(so[x], gr);
  for (int i = 0; i < g[x].size(); ++i) {
    int v = g[x][i].first;
    if (v != fa[x] && v != so[x]) {
      Dfs1(v, v);
      ++cnt[x][g[x][i].second];
    }
  }
  Wish(x);
}

void Flip(int u) {
  for (int x = u; x; x = fa[tp[x]]) {
    ans -= S::Query(dfn[tp[x]] + 1, dfn[x]);
    ans -= Chk(tp[x]) + (tp[x] != x? Chk(x) : 0);
  }
  for (int x = u; x; x = fa[tp[x]]) {
    int z = tp[x];
    int y = fa[z];
    if (y) {
      int w = S::Ask(1, 1, n, dfn[z]);
      --cnt[y][w];
      ++cnt[y][w ^ 1];
      Wish(y);
      S::Modify(1, 1, n, dfn[y]);
    }
    S::Flip(1, 1, n, dfn[tp[x]], dfn[x]);
  }
  for (int x = u; x; x = fa[tp[x]]) {
    ans += S::Query(dfn[tp[x]] + 1, dfn[x]);
    ans += Chk(tp[x]) + (tp[x] != x? Chk(x) : 0);
  }
}

int main() {
  scanf("%d", &tc);
  for (; tc--; ) {
    scanf("%d", &n);
    for (int i = 1, x, y, z; i < n; ++i) {
      scanf("%d%d%d", &x, &y, &z);
      g[x].push_back(pair<int, bool>(y, z));
      g[y].push_back(pair<int, bool>(x, z));
    }

    Dfs0(1);
    Dfs1(1, 1);
    S::Build(1, 1, n);

    ans = 0;
    for (int i = 1; i <= n; ++i) {
      if (!so[i]) {
        ++ans;
        if (tp[i] != i) {
          ans += Chk(tp[i]);
          ans += S::Query(dfn[tp[i]] + 1, dfn[i]);
        }
      }
    }
    
    scanf("%d", &nq);
    for (int u, v; nq--; ) {
      scanf("%d%d", &u, &v);
      Flip(u);
      Flip(v);
      printf("%d\n", (n == 1)? 0 : n - ans + 1);
    }
    
    // remember to clear up
    clk = 0;
    for (int i = 1; i <= n; ++i) {
      g[i].clear();
      so[i] = tp[i] = nat[i] = cnt[i][0] = cnt[i][1] = 0;
    }
  }
  
  return 0;
}
View Code

 

Sonya and Queries

(被象飞了,写了4个小时)

我们暂且不考虑操作$7$。如果只有前六个操作,大概就是要实现一个数据结构,支持:1,在原树的基础上$link/cut$;2,整个联通块加上一个正数;3,整个联通块赋值为$0$;4,联通块求和;5,单点加,单点查询。由于只有子树的相关操作,所以这些都可以用$ETT$直接解决,也十分好写,只要实现一棵平衡树维护双括号序,$link/cut$时只要$merge/split$就行了。

操作$7$,其要求所有有值的联通块构成的虚树的边数(也就是点数)。考虑如果我们现在有一棵以联通块为点构成的树$T'$,记$cnt_x$表示$x$的子树里有值的点的个数,令$tot$为$T'$点的个数,则要求的虚树的点数为:$tot - \sum\limits_{i = 1}^{tot}[cnt_i = 0] - \sum\limits_{i = 1}^{tot}[cnt_i = cnt_{root}] + 1$。如果某一个点是$0$是$1$的状态改变了,相当于对该点的祖先链的$cnt$执行$+1$或$-1$,实现起来简单点的话可以直接轻重链剖分,用线段树维护树链,并且记录区间内最大值及其个数,最小值及其个数,$\sum\limits_{i = 1}^{tot}[cnt_i = 0]$就是全局最小值的个数(如果最小值是$0$的话),$\sum\limits_{i = 1}^{tot}[cnt_i = cnt_{root}]$就是全局最大值的个数。回到原树上,我们把每个联通块的贡献算到该联通块的根上,即只用其根来标志这个联通块,每个$1$~$6$操作后可能会改掉某个联通块是否有值的状态,也可能会增加或取消某个用于标记联通块的点,这些都扔到线段树上改改就行了。

时间复杂度$O(nlog^2n)$,空间复杂度$O(n)$。

#include <bits/stdc++.h>

using namespace std;

const int N = 2.5e5 + 5;
const int INF = 1e9 + 7;
const pair<int, int> PINF = pair<int, int>(INF, 0);
const pair<int, int> PFNI = pair<int, int>(-INF, 0);

int n, nq, tot, clk;
int tv[N * 2], bel[N], fw[N], valsub[N], ival[N];
vector<int> g[N];
int fa[N], si[N], so[N], tp[N], dep[N], dfn[N], li[N];
bool usd[N];
char av[N];

namespace S {
  struct Node {
    pair<int, int> mn, mx;
    int tagadd, cnt;
  } node[N * 4];

  pair<int, int> Comb_min(pair<int, int> a, pair<int, int> b) {
    if (a.first < b.first) {
      return a;
    } else if (a.first > b.first) {
      return b;
    } else {
      return pair<int, int>(a.first, a.second + b.second);
    }
  }

  pair<int, int> Comb_max(pair<int, int> a, pair<int, int> b) {
    if (a.first > b.first) {
      return a;
    } else if (a.first < b.first) {
      return b;
    } else {
      return pair<int, int>(a.first, a.second + b.second);
    }
  }
  
  void Up(int t) {
    node[t].mn = Comb_min(node[t << 1].mn, node[t << 1 | 1].mn);
    node[t].mx = Comb_max(node[t << 1].mx, node[t << 1 | 1].mx);
  }

  void Uadd(int t, int _v) {
    Node &nw = node[t];
    nw.tagadd += _v;
    nw.cnt += _v;
    nw.mn.first += _v;
    nw.mx.first += _v;
  }

  void Down(int t) {
    if (node[t].tagadd) {
      Uadd(t << 1, node[t].tagadd);
      Uadd(t << 1 | 1, node[t].tagadd);
      node[t].tagadd = 0;
    }
  }
  
  void Build(int t, int l, int r) {
    if (l == r) {
      node[t].cnt = valsub[li[l]];
      node[t].mn = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PINF;
      node[t].mx = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PFNI;
      return;
    }
    int md = (l + r) >> 1;
    Build(t << 1, l, md);
    Build(t << 1 | 1, md + 1, r);
    Up(t);
  }
  
  void Modify(int t, int l, int r, int L, int R, int _v) {
    if (L <= l && r <= R) {
      Uadd(t, _v);
      return;
    }
    int md = (l + r) >> 1;
    Down(t);
    if (L <= md) Modify(t << 1, l, md, L, R, _v);
    if (R > md) Modify(t << 1 | 1, md + 1, r, L, R, _v);
    Up(t);
  }

  void Change_usd(int t, int l, int r, int x) {
    if (l == r) {
      node[t].mn = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PINF;
      node[t].mx = usd[li[l]]? pair<int, int>(node[t].cnt, 1) : PFNI;
      return;
    }
    int md = (l + r) >> 1;
    Down(t);
    if (x <= md) Change_usd(t << 1, l, md, x);
    else Change_usd(t << 1 | 1, md + 1, r, x);
    Up(t);
  }
  
  pair<int, int> Query_min(int t, int l, int r, int L, int R) {
    if (L <= l && r <= R) {
      return node[t].mn;
    }
    int md = (l + r) >> 1;
    Down(t);
    pair<int, int> re = PINF;
    if (L <= md) re = Comb_min(re, Query_min(t << 1, l, md, L, R));
    if (R > md) re = Comb_min(re, Query_min(t << 1 | 1, md + 1, r, L, R));
    return re;
  }

  pair<int, int> Query_max(int t, int l, int r, int L, int R) {
    if (L <= l && r <= R) {
      return node[t].mx;
    }
    int md = (l + r) >> 1;
    Down(t);
    pair<int, int> re = PFNI;
    if (L <= md) re = Comb_max(re, Query_max(t << 1, l, md, L, R));
    if (R > md) re = Comb_max(re, Query_max(t << 1 | 1, md + 1, r, L, R));
    return re;
  }
  
}


int li2[N * 2], st[N * 2], ed[N * 2], clk2;

namespace T {
  struct Node {
    int lc, rc, p, sz, rnd, tagcov;
    long long val, sum, tagadd;
  } node[N * 2];

  void Up(int t) {
    node[t].sum = node[t].val + node[node[t].lc].sum + node[node[t].rc].sum;
    node[t].sz = 1 + node[node[t].lc].sz + node[node[t].rc].sz;
  }

  void Ucov(int t) {
    Node &nw = node[t];
    nw.val = nw.sum = nw.tagadd = 0;
    nw.tagcov = 1;
  }

  void Uadd(int t, long long _v) {
    Node &nw = node[t];
    nw.val += _v;
    nw.tagadd += _v;
    nw.sum += _v * nw.sz;
  }
  
  void Down(int t) {
    Node &nw = node[t];
    if (nw.tagcov) {
      if (nw.lc) Ucov(nw.lc);
      if (nw.rc) Ucov(nw.rc);
      nw.tagcov = 0;
    }
    if (nw.tagadd) {
      if (nw.lc) Uadd(nw.lc, nw.tagadd);
      if (nw.rc) Uadd(nw.rc, nw.tagadd);
      nw.tagadd = 0;
    }
  }

  void Split(int t, int k, int &x, int &y) {
    if (!k) {
      x = 0;
      y = t;
      return;
    }
    Down(t);
    if (k <= node[node[t].lc].sz) {
      y = t;
      node[node[t].lc].p = 0;
      Split(node[t].lc, k, x, node[t].lc);
      node[node[t].lc].p = t;
    } else {
      x = t;
      node[node[t].rc].p = 0;
      Split(node[t].rc, k - node[node[t].lc].sz - 1, node[t].rc, y);
      node[node[t].rc].p = t;
    }
    Up(t);
  }

  int Merge(int x, int y) {
    if (!x || !y) {
      return x | y;
    }
    if (node[x].rnd < node[y].rnd) {
      Down(x);
      node[x].rc = Merge(node[x].rc, y);
      node[node[x].rc].p = x;
      Up(x);
      return x;
    } else {
      Down(y);
      node[y].lc = Merge(x, node[y].lc);
      node[node[y].lc].p = y;
      Up(y);
      return y;
    }
  }

  int Rank(int x) {
    int ret = node[node[x].lc].sz;
    while (node[x].p) {
      if (x == node[node[x].p].rc) {
        x = node[x].p;
        ret += node[node[x].lc].sz + 1;
      } else {
        x = node[x].p;
      }
    }
    return ret + 1;
  }

  void Roll(int x) {
    if (node[x].p) {
      Roll(node[x].p);
    }
    Down(x);
  }
  
  void Wish(int x, long long _v) {
    Roll(x);
    node[x].val = _v;
    while (x) {
      Up(x);
      x = node[x].p;
    }
  }

  long long Thanks(int x) {
    Roll(x);
    return node[x].val;
  }
  
  int Find_root(int x) {
    while (node[x].p) {
      x = node[x].p;
    }
    return x;
  }

  int Gen(int x) {
    while (node[x].lc) {
      x = node[x].lc;
    }
    return x;
  }
  
}


void Dfs0(int x) {
  si[x] = 1;
  valsub[x] = ival[x] > 0;
  for (int i = 0; i < g[x].size(); ++i) {
    int e = g[x][i], v = tv[e];
    if (v == fa[x]) continue;
    fa[v] = x;
    dep[v] = dep[x] + 1;
    bel[e / 2] = v;
    fw[v] = e / 2;
    Dfs0(v);
    si[x] += si[v];
    if (si[v] > si[so[x]]) so[x] = v;
    valsub[x] += valsub[v];
  }
}

void Dfs1(int x, int gr) {
  dfn[x] = ++clk;
  li[clk] = x;
  
  st[x] = ++clk2;
  li2[clk2] = x;
  T::node[clk2] = (T::Node){ 0, 0, 0, 1, rand(), 0, ival[x], ival[x], 0 };
  
  tp[x] = gr;
  if (so[x]) {
    Dfs1(so[x], gr);
  }
  for (int i = 0; i < g[x].size(); ++i) {
    int e = g[x][i], v = tv[e];
    if (v != fa[x] && v != so[x]) {
      Dfs1(v, v);
    }
  }

  ed[x] = ++clk2;
  li2[clk2] = x;
  T::node[clk2] = (T::Node){ 0, 0, 0, 1, rand(), 0, ival[x], ival[x], 0 };
  T::Merge(st[x], ed[x]);
}

void Chain_update(int x, int _v) {
  while (x) {
    S::Modify(1, 1, n, dfn[tp[x]], dfn[x], _v);
    x = fa[tp[x]];
  }
}

void Cut_from_father(int x) {
  int l = st[x], r = ed[x];
  int rkl = T::Rank(l), rkr = T::Rank(r);
  int rt = T::Find_root(l);
  long long lsv = T::node[rt].sum;
  static int a, b, c;
  T::Split(rt, rkr, b, c);
  T::Split(b, rkl - 1, a, b);
  rt = T::Merge(a, c);
  usd[x] = 1;
  ++tot;
  S::Change_usd(1, 1, n, dfn[x]);
  if (T::node[b].sum > 0) {
    Chain_update(x, 1);
  }
  if (lsv > 0 && T::node[rt].sum == 0) {
    int ge = li2[T::Gen(rt)];
    Chain_update(ge, -1);
  }
}

void Link_to_father(int x) {
  int y = fa[x];
  int rkl = T::Rank(st[y]);
  int rt = T::Find_root(st[y]);
  long long lsv = T::node[rt].sum;
  static int a, b, c;
  T::Split(rt, rkl, a, c);
  b = T::Find_root(st[x]);
  if (T::node[b].sum > 0) {
    Chain_update(x, -1);
  }
  usd[x] = 0;
  --tot;
  S::Change_usd(1, 1, n, dfn[x]);
  rt = T::Merge(T::Merge(a, b), c);
  if (lsv == 0 && T::node[rt].sum > 0) {
    int ge = li2[T::Gen(rt)];
    Chain_update(ge, 1);
  }
}

int main() {
  scanf("%*d%d%d", &n, &nq);
  tot = n;
  for (int i = 1, x, y; i < n; ++i) {
    scanf("%d%d", &x, &y);
    tv[i * 2] = y;
    g[x].push_back(i * 2);
    tv[i * 2 + 1] = x;
    g[y].push_back(i * 2 + 1);
  }
  
  scanf("%s", av + 1);
  for (int i = 1; i <= n; ++i) {
    scanf("%d", &ival[i]);
    usd[i] = 1;
  }
  
  Dfs0(1);
  Dfs1(1, 1);
  S::Build(1, 1, n);
  
  for (int i = 1; i < n; ++i) {
    av[i] -= '0';
    if (av[i] == 0) {
      Link_to_father(bel[i]);
    }
  }
  
  for (int ty, x, y; nq--; ) {
    scanf("%d", &ty);
    if (ty != 7) {
      scanf("%d", &x);
    }

    if (ty == 1) {
      av[x] ^= 1;
      if (av[x] == 1) {
        Cut_from_father(bel[x]);
      } else {
        Link_to_father(bel[x]);
      }
    }

    if (ty == 2) {
      scanf("%d", &y);
      if (y != 0) {
        int rt = T::Find_root(st[x]);
        long long lsv = T::node[rt].sum;
        T::Uadd(rt, y);
        if (!lsv) {
          int ge = li2[T::Gen(rt)];
          Chain_update(ge, 1);
        }
      }
    }

    if (ty == 3) {
      int rt = T::Find_root(st[x]);
      long long sum = T::node[rt].sum / 2;
      T::Ucov(rt);
      T::Wish(st[x], sum);
      T::Wish(ed[x], sum);
    }

    if (ty == 4) {
      printf("%lld\n", T::Thanks(st[x]));
    }

    if (ty == 5) {
      int rt = T::Find_root(st[x]);
      long long sum = T::node[rt].sum;
      printf("%lld\n", sum / 2);
    }

    if (ty == 6) {
      int rt = T::Find_root(st[x]);
      long long lsv = T::node[rt].sum;
      T::Ucov(rt);
      if (lsv) {
        int ge = li2[T::Gen(rt)];
        Chain_update(ge, -1);
      }
    }

    if (ty == 7) {
      int res = tot;
      res -= S::Query_max(1, 1, n, 1, n).second - 1;
      pair<int, int> mim = S::Query_min(1, 1, n, 1, n);
      if (mim.first == 0) {
        res -= mim.second;
      }
      printf("%d\n", max(0, res - 1));
    }
    
    if (ty == 4 || ty == 5 || ty== 7) {
      fflush(stdout);
    }
  }
  
  return 0;
}
View Code

 

posted @ 2019-04-15 19:05  Dance_Of_Faith  阅读(395)  评论(2编辑  收藏  举报