ZJOI2018 Day1 简要题解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wxh010910/article/details/79682014

line

一个结论是, Lk(T) 的每个节点在原树上是一个不超过 k+1 个点的连通块。

考虑用括号序列枚举所有不同的有根树,利用树哈希来去重,这样的树大概 1000 个,然后答案就是它的贡献乘上它在原树上的出现次数。

在原树上的出现次数可以直接状压DP,可以加一个剪枝,就是叶子单独提出来最后用组合数计算,注意要除以自同构的方案数。

一棵有根树 T 的贡献可以计算 Lk(T) 的节点数 VLk(T) ,然后枚举它的所有连通子树,去掉属于它们的节点数。

考虑如何计算 VLk(G) ,记 di 表示 i 的度数:

  • VL(G)=EG

  • VL2(G)=di(di1)2

  • VL3(G)=di(di1)(di2)2+e(du1)(dv1)

  • VL4(G)=VL3(L(G)) ,考虑计算 L(G) 的每个点的度数,不难发现对于 G 的一条边 (u,v) ,它在 L(G) 中度数为 du+dv2 ,而在 L(G) 中的一条边表示有公共点的一对边,枚举公共点,需要算 u<v(du1)(dv1) ,这个可以通过计算和的平方以及平方的和来计算。

  • 其他情况暴力求出 Lk4(G)

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 5005;
const int M = 1000005;
const int mod = 998244353;

struct Tree {
  int n, val, slf, lef;
  vector <int> adj;
} tre[N];

struct Graph {
  vector <pii> E;
  int V;
} G;

int n, m, all, ans, cur, rot, slf, tot, fac[N], siz[N], bin[N][15], f[N][N];
unordered_map <int, int> ind, sam;
vector <int> adj[N], val[M];
vector <string> sub;
bool seq[N];

inline int Qow(int x, int y) {
  int r = 1;
  for (; y; y >>= 1, x = 1LL * x * x % mod) {
    if (y & 1) {
      r = 1LL * r * x % mod;
    }
  }
  return r;
}

inline int FindTree() {
  int x = ++tot;
  for (++cur; seq[cur]; adj[x].pb(FindTree()));
  ++cur;
  return x;
}

inline string FindHash(int x, int p) {
  vector <string> chd;
  string ret = "1";
  for (auto y : adj[x]) {
    if (y != p) {
      chd.pb(FindHash(y, x));
    }
  }
  sort(chd.begin(), chd.end());
  for (int i = 0, j = 0; i < chd.size(); ) {
    for (; j < chd.size() && chd[j] == chd[i]; ++j);
    slf = 1LL * slf * fac[j - i] % mod;
    for (; i < j; ret += chd[i], ++i);
  }
  if (x == 1) {
    sub = chd;
  }
  return ret + "0";
}

inline int ToInt(string s) {
  int ret = 0;
  for (int i = 0; i < s.length(); ++i) {
    ret = ret << 1 | s[i] - '0';
  }
  return ret;
}

inline Graph L(Graph G) {
  static vector <int> adj[M];
  Graph H;
  H.V = G.E.size();
  for (int i = 0; i < G.V; ++i) {
    adj[i].clear();
  }
  for (int i = 0; i < G.E.size(); ++i) {
    adj[G.E[i].X].pb(i), adj[G.E[i].Y].pb(i);
  }
  for (int i = 0; i < G.V; ++i) {
    for (int j = 0; j < adj[i].size(); ++j) {
      for (int k = 0; k < j; ++k) {
        H.E.pb(mp(adj[i][k], adj[i][j]));
      }
    }
  }
  return H;
}

inline void FindSize(int x, int p) {
  siz[x] = 1;
  for (auto y : adj[x]) {
    if (y != p) {
      FindSize(y, x), siz[x] += siz[y];
    }
  }
}

inline void FindRoot(int x, int p, int n) {
  siz[x] = n - siz[x];
  for (auto y : adj[x]) {
    if (y != p) {
      CheckMax(siz[x], siz[y]), FindRoot(y, x, n);
    }
  }
  if (!rot || siz[rot] > siz[x]) {
    rot = x;
  }
}

inline string FindSubtree(int x, int p, int s) {
  vector <string> chd;
  string ret = "1";
  for (auto y : adj[x]) {
    if (y != p && (s >> y - 1 & 1)) {
      chd.pb(FindSubtree(y, x, s));
    }
  }
  sort(chd.begin(), chd.end());
  for (auto s : chd) {
    ret += s;
  }
  return ret + "0";
}

inline void Solve(int n) {
  for (int i = 1; i <= n; ++i) {
    adj[i].clear();
  }
  tot = 0, cur = slf = 1, sub.clear(), FindTree();
  string hsh = FindHash(1, 0);
  if (ind[ToInt(hsh)]) {
    return ;
  }
  int x = ind[ToInt(hsh)] = ++all;
  tre[x].n = n, tre[x].slf = slf;
  for (auto s : sub) {
    if (s == "10") {
      ++tre[x].lef;
    } else {
      tre[x].adj.pb(ind[ToInt(s)]);
    }
  }
  for (int i = 1; i <= n; ++i) {
    for (auto j : adj[i]) {
      if (j > i) {
        adj[j].pb(i);
      }
    }
  }
  rot = 0, FindSize(1, 0), FindRoot(1, 0, n);
  sub.clear(), hsh = FindHash(rot, 0);
  if (sam[ToInt(hsh)]) {
    tre[x].val = tre[sam[ToInt(hsh)]].val;
    return ;
  }
  sam[ToInt(hsh)] = x;
  G.V = n, G.E.clear();
  for (int i = 1; i <= n; ++i) {
    for (auto j : adj[i]) {
      if (i < j) {
        G.E.pb(mp(i - 1, j - 1));
      }
    }
  }
  if (m >= 4) {
    for (int i = 1; i <= m - 4; ++i) {
      G = L(G);
    }
    int ret = 0, n = G.V, m = G.E.size();
    vector <int> d(n, 0), e(m, 0);
    for (int i = 0; i < n; ++i) {
      val[i].clear();
    }
    for (int i = 0; i < m; ++i) {
      ++d[G.E[i].X], ++d[G.E[i].Y];
    }
    for (int i = 0; i < m; ++i) {
      e[i] = d[G.E[i].X] + d[G.E[i].Y] - 2;
      ret = (1LL * e[i] * (e[i] - 1) % mod * (e[i] - 2) % mod * (mod + 1) / 2 + ret) % mod;
      val[G.E[i].X].pb(e[i] - 1), val[G.E[i].Y].pb(e[i] - 1);
    }
    for (int i = 0; i < n; ++i) {
      if (val[i].size() > 1) {
        int sum = 0, sqr = 0;
        for (int j = 0; j < val[i].size(); ++j) {
          sum = (sum + val[i][j]) % mod, sqr = (1LL * val[i][j] * val[i][j] + sqr) % mod;
        }
        sum = 1LL * sum * sum % mod;
        ret = (1LL * (sum - sqr + mod) * (mod + 1) / 2 + ret) % mod;
      }
    }
    tre[x].val = ret;
  } else {
    for (int i = 1; i <= m; ++i) {
      G = L(G);
    }
    tre[x].val = G.V;
  }
  for (int i = 1; i < (1 << n) - 1; ++i) {
    int t = 0;
    for (int j = 0; j < n; ++j) {
      if (i >> j & 1) {
        t = j + 1;
        break;
      }
    }
    string s = FindSubtree(t, 0, i);
    if (s.length() == __builtin_popcount(i) << 1) {
      tre[x].val = (tre[x].val - tre[ind[ToInt(s)]].val + mod) % mod;
    }
  }
}

inline void DFS(int x, int d, int c, int n) {
  if (x == n << 1) {
    Solve(n);
  } else {
    if (d) {
      seq[x] = false, DFS(x + 1, d - 1, c, n);
    }
    if (c < n) {
      seq[x] = true, DFS(x + 1, d + 1, c + 1, n);
    }
  }
}

inline void FindAllTree(int n) {
  seq[1] = true, seq[n << 1] = false, DFS(2, 0, 1, n);
}

inline int DP(vector <int> a, vector <int> b, int c) {
  static int g[N];
  int n = a.size(), m = b.size();
  if (n < m + c) {
    return 0;
  }
  for (int i = 0; i < 1 << m; ++i) {
    g[i] = 0;
  }
  g[0] = 1;
  for (auto x : a) {
    for (int i = (1 << m) - 1; ~i; --i) {
      for (int j = 0; j < m; ++j) {
        if (!(i >> j & 1)) {
          g[i | 1 << j] = (1LL * f[x][b[j]] * g[i] + g[i | 1 << j]) % mod;
        }
      }
    }
  }
  return 1LL * g[(1 << m) - 1] * bin[n - m][c] % mod * fac[c] % mod;
}

inline void DP(int x, int p) {
  vector <int> chd;
  for (auto y : adj[x]) {
    if (y != p) {
      DP(y, x), chd.pb(y);
    }
  }
  for (int i = 1; i <= all; ++i) {
    f[x][i] = DP(chd, tre[i].adj, tre[i].lef);
  }
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), Read(m), fac[0] = 1;
  for (int i = 1; i <= m + 1; ++i) {
    FindAllTree(i), fac[i] = 1LL * fac[i - 1] * i % mod;
  }
  for (int i = 1; i <= n; ++i) {
    adj[i].clear();
  }
  for (int i = 1, x, y; i < n; ++i) {
    Read(x), Read(y);
    adj[x].pb(y), adj[y].pb(x);
  }
  for (int i = 0; i <= n; ++i) {
    bin[i][0] = 1;
    for (int j = 1; j <= m && j <= i; ++j) {
      bin[i][j] = (bin[i - 1][j] + bin[i - 1][j - 1]) % mod;
    }
  }
  DP(1, 0);
  for (int i = 1; i <= all; ++i) {
    tre[i].val = 1LL * tre[i].val * Qow(tre[i].slf, mod - 2) % mod;
  }
  for (int i = 1; i <= n; ++i) {
    for (int j = 1; j <= all; ++j) {
      ans = (1LL * f[i][j] * tre[j].val + ans) % mod;
    }
  }
  printf("%d\n", ans);
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

history

首先每个节点是独立的,记 sizi 表示 i 这棵子树的 ai 之和,特别地,在计算节点 i 的时候, sizi=ai ,那么它的贡献为 min(sizi1,2(sizimax(sizi))

2sizi>sizparii 认为是 LCTparipreferred child ,那么不难发现轻边个数是 O(logai) 条的,所以类似 LCT 暴力维护即可。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

const int N = 400005;

int n, m, f[N], par[N], siz[N], chd[N][2];
LL ans, a[N], tag[N], sum[N];
vector <int> adj[N];

inline void DFS(int x) {
  LL max_sum = sum[x] = a[x];
  int pos = x;
  for (int i = 0; i < adj[x].size(); ++i) {
    int y = adj[x][i];
    if (y != par[x]) {
      par[y] = f[y] = x, DFS(y), sum[x] += sum[y];
      if (CheckMax(max_sum, sum[y])) {
        pos = y;
      }
    }
  }
  if (max_sum << 1 > sum[x]) {
    ans += sum[x] - max_sum << 1;
    if (pos != x) {
      chd[x][1] = pos, siz[x] = siz[pos] + 1;
    } else {
      siz[x] = 1;
    }
  } else {
    ans += sum[x] - 1, siz[x] = 1;
  }
}

inline void Modify(int x, LL y) {
  sum[x] += y, tag[x] += y;
}

inline bool IsRoot(int x) {
  return chd[f[x]][0] != x && chd[f[x]][1] != x;
}

inline void PushUp(int x) {
  siz[x] = siz[chd[x][0]] + siz[chd[x][1]] + 1;
}

inline void PushDown(int x) {
  if (tag[x]) {
    Modify(chd[x][0], tag[x]), Modify(chd[x][1], tag[x]), tag[x] = 0;
  }
}

inline void Rotate(int x) {
  int y = f[x], z = f[y], k = chd[y][1] == x;
  if (!IsRoot(y)) {
    chd[z][chd[z][1] == y] = x;
  }
  f[chd[y][k] = chd[x][!k]] = y;
  f[f[chd[x][!k] = y] = x] = z;
  PushUp(y), PushUp(x);
}

inline void Splay(int x) {
  static int top, sta[N];
  sta[top = 1] = x;
  for (int i = x; !IsRoot(i); sta[++top] = i = f[i]);
  for (; top; PushDown(sta[top--]));
  for (int y = f[x], z = f[y]; !IsRoot(x); Rotate(x), y = f[x], z = f[y]) {
    if (!IsRoot(y)) {
      Rotate((chd[y][1] == x) == (chd[z][1] == y) ? y : x);
    }
  }
}

inline int FindLeft(int x) {
  if (!x) {
    return 0;
  }
  for (; chd[x][0]; x = chd[x][0]);
  Splay(x);
  return x;
}

inline void Access(int x, int y) {
  static int cur[N], lst[N], sta[N];
  int top = 0;
  cur[1] = x, a[x] += y;
  for (int t; x; x = f[x]) {
    Splay(x), t = FindLeft(chd[x][1]), Splay(x), chd[x][1] = 0;
    PushUp(x), Modify(x, y), PushDown(x);
    ++top, lst[top] = t, sta[top] = x;
    chd[x][1] = t, cur[top + 1] = FindLeft(x), Splay(x);
  }
  for (int i = 1; i <= top; ++i) {
    int x = sta[i];
    Splay(x);
    if (lst[i]) {
      ans -= sum[x] - y - sum[lst[i]] << 1;
    } else if (a[x] - (i == 1 ? y : 0) << 1 > sum[x] - y) {
      ans -= sum[x] - y - a[x] + (i == 1 ? y : 0) << 1;
    } else {
      ans -= sum[x] - y - 1;
    }
    if (lst[i]) {
      if (sum[lst[i]] << 1 > sum[x]) {
        ans += sum[x] - sum[lst[i]] << 1;
      } else {
        if (i != 1) {
          Splay(cur[i]);
          if (sum[cur[i]] << 1 > sum[x]) {
            ans += sum[x] - sum[cur[i]] << 1;
            chd[x][1] = cur[i], PushUp(x);
          } else if (a[x] << 1 > sum[x]) {
            ans += sum[x] - a[x] << 1;
            chd[x][1] = 0, PushUp(x);
          } else {
            ans += sum[x] - 1;
            chd[x][1] = 0, PushUp(x);
          }
        } else {
          if (a[x] << 1 > sum[x]) {
            ans += sum[x] - a[x] << 1;
            chd[x][1] = 0, PushUp(x);
          } else {
            ans += sum[x] - 1;
            chd[x][1] = 0, PushUp(x);
          }
        }
      }
    } else {
      if (i != 1) {
        Splay(cur[i]);
        if (sum[cur[i]] << 1 > sum[x]) {
          ans += sum[x] - sum[cur[i]] << 1;
          chd[x][1] = cur[i], PushUp(x);
        } else if (a[x] << 1 > sum[x]) {
          ans += sum[x] - a[x] << 1;
        } else {
          ans += sum[x] - 1;
        }
      } else {
        if (a[x] << 1 > sum[x]) {
          ans += sum[x] - a[x] << 1;
        } else {
          ans += sum[x] - 1;
        }
      }
    }
  }
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  Read(n), Read(m);
  for (int i = 1; i <= n; ++i) {
    Read(a[i]);
  }
  for (int i = 1, x, y; i < n; ++i) {
    Read(x), Read(y);
    adj[x].pb(y), adj[y].pb(x);
  }
  DFS(1);
  printf("%lld\n", ans);
  for (int i = 1, x, y; i <= m; ++i) {
    Read(x), Read(y), Access(x, y);
    printf("%lld\n", ans);
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}

maze

首先能构造出一个为 k 的答案,即 i 的第 j 条出边连向 im+jmodk ,显然符合要求。

考虑去除等价的节点,记 f(i) 表示 i0 的最小步数,两个节点 i,j 等价的充要条件是 f(i)=f(j),i×mf(i)=j×mf(j)

如果 (m,k)=1 ,那么答案显然是 k ,考虑计算其他情况,首先 0 显然是独立的。

考虑按照 f(i) 从小到大的顺序一层层计算,将 1,2,,k1m ,那么 f(i)=1 的条件就是乘 m 后的值在 [km+1,k] 这个区间内,将这个区间内的数去重后加入答案。接下来找 f(i)=2 的,那么再乘上 m 删去 [km2+1,k] 内的数。

快速维护这个过程,记 solve(l,m,k) 表示当前考虑 1,2,,l 这些数的答案,记 d=(m,k) ,如果 d=1 就可以停止了。

如果 l>kd ,那么 im 能取遍所有 kd 个值,剩下的是 (0,km(kl)]d 的倍数,删去了 m(kl)d 个数,然后递归计算。

否则 immodk 互不相同,每个数都是一个等价类。

#include <bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define Debug(...) fprintf(stderr, __VA_ARGS__)

typedef long long LL;
typedef long double LD;
typedef unsigned int uint;
typedef pair <int, int> pii;
typedef unsigned long long uLL;

template <typename T> inline void Read(T &x) {
  char c = getchar();
  bool f = false;
  for (x = 0; !isdigit(c); c = getchar()) {
    if (c == '-') {
      f = true;
    }
  }
  for (; isdigit(c); c = getchar()) {
    x = x * 10 + c - '0';
  }
  if (f) {
    x = -x;
  }
}

template <typename T> inline bool CheckMax(T &a, const T &b) {
  return a < b ? a = b, true : false;
}

template <typename T> inline bool CheckMin(T &a, const T &b) {
  return a > b ? a = b, true : false;
}

inline LL Solve(LL l, LL m, LL k) {
  LL d = __gcd(m, k);
  if (d == 1) {
    return l;
  } else {
    if (l > k / d) {
      if (k / (k - l) >= m) {
        return m / d * (k - l) + Solve(k / d - m / d * (k - l), m, k / d);
      } else {
        return k / d;
      }
    } else {
      return l;
    }
  }
}

int Main() {
  LL m, k;
  Read(m), Read(k), printf("%lld\n", 1 + Solve(k - 1, m, k));
  return 0;
}

int main() {
#ifdef wxh010910
  freopen("d.in", "r", stdin);
#endif
  int T;
  for (Read(T); T; --T) {
    Main();
  }
#ifdef wxh010910
  Debug("My Time: %.3lfms\n", (double)clock() / CLOCKS_PER_SEC);
#endif
  return 0;
}
阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页