Loading

板子

如果什么都记,必然很难找。

所以大型数据结构就不记了。

快读 & 快写

int read() {
  int x = 0, f = 1; char c = getchar();
  while (c < '0' || c > '9') { if(c == '-') f = -1; c = getchar(); }
  while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
  return x * f;
}
void write(int x) {
  if(x < 0) putchar('-'), x = -x;
  if(x > 9) write(x / 10);
  putchar(x % 10 + '0');
  return;
}

重载不完全的矩阵

#include <initializer_list>
struct matrix {
  int n, m; std::vector<std::vector<int>> a;
  matrix() {}
  matrix(int n, int m) : n(n), m(m) {
    a.assign(n, std::vector<int>(m));
  }
  matrix(int n) : n(n), m(m) {
    a.assign(n, std::vector<int>(m));
    for (int i = 0; i < n; i++) a[i][i] = 1;
  }
  auto operator [] (int i) {
    return a[i];
  }
  matrix operator * (const matrix &tmp) {
    assert(m == tmp.n);
    matrix t(n, tmp.m);
    for (int i = 0; i < n; i++)
      for (int j = 0; j < tmp.m; j++)
        for (int k = 0; k < m; k++)
          t[i][j] = (t[i][j] + 1ll * a[i][k] * tmp.a[k][j] % mod) % mod;
    return t;
  }
  matrix operator ^ (int tmp) const {
    assert(n == m);
    matrix ans(n), t = *this;
    for (; tmp; tmp >>= 1) {
      if(tmp & 1) ans = ans * t;
      t = t * t;
    }
    return ans;
  }
  matrix operator + (const matrix &tmp) const {
    assert(n == tmp.n && m == tmp.m);
    matrix ans = *this;
    for (int i = 0; i < n; i++)
      for (int j = 0; j < m; j++)
        ans[i][j] = add(a[i][j], tmp.a[i][j]);
    return ans;
  }
  void print(string s) {
    cout << s << ":\n";
    // printf("n=%d m=%d\n", n, m);
    for (int i = 0; i < n; i++) {
      for (int j = 0; j < m; j++) {
        printf("%d ", a[i][j]);
      } printf("\n");
    } printf("\n");
  }
  bool operator == (const matrix &tmp) const {
    assert(n == tmp.n && m == tmp.m);
    for (int i = 0; i < n; i++)
      for (int j = 0; j < n; j++) if(a[i][j] != tmp.a[i][j]) return 0;
    return 1;
  }
};

inv

int invn[M];
void pre(int n) { 
  invn[1] = 1;
  for (int i = 2; i <= n; i++)
    invn[i] = 1ll * (p - p/i) * invn[p%i] %p;
}

void exgcd(int a, int b, int &x, int &y) {
  if (!b) return x = 1, y = 0, void();
  exgcd(b, a % b, y, x), y -= a / b * x;
}
int inv(int x, int p) { return exgcd(x, p, x, *new int), (x % p + p) % p; }

\(\varphi(n)\)

int phi(int n) {
  int ans = n;
  for (int i = 2; i * i <= n; i++) if(n % i == 0) {
    ans = ans / i * (i-1); while (n % i == 0) n /= i;
  }
  if(n > 1) ans = ans / n * (n-1);
  return ans; 
}

mod

int qpow(int a, int b) {
  long long ans = 1ll;
  for (; b; b >>= 1) {if(b & 1) ans = 1ll * ans * a % mod; a = 1ll * a * a % mod;}
  return ans;
}
int inv(int k) {return qpow(k, mod - 2);}
int add(int a, int b) {a += b; return a > mod ? a-mod : a;}
int del(int a, int b) {a -= b; return a < 0 ? a+mod : a;}
void addn(int &x, int y) {x += y; if(x > mod) x -= mod;}
int fact(int x) {int ans = 1; for (int i = 1; i <= x; i++) ans = 1ll * ans * i % mod; return ans;}
int fac[M], invfac[M], invn[M];
void pre(int n = 250000) {
  fac[0] = invfac[0] = fac[1] = invfac[1] = invn[1] = 1;
  for (int i = 2; i <= n; i++)
    invn[i] = 1ll * (mod - mod/i) * invn[mod%i] % mod,
    fac[i] = 1ll * fac[i-1] * i % mod,
    invfac[i] = 1ll * invfac[i-1] * invn[i] % mod;
}
int C(int n, int m) {return m > n ? 0 : 1ll * fac[n] * invfac[m] % mod * invfac[n-m] % mod;}

Mint

(来源 https://www.cnblogs.com/Rainbowsjy/p/13907092.html

const int mod = 998244353;
struct modint {
  int x;
  modint(int o = 0) {x = o;}
  modint &operator = (int o) {return x = o, *this;}
  modint &operator += (modint o) {return x = x+o.x >= mod ? x+o.x-mod : x+o.x, *this;}
  modint &operator -= (modint o) {return x = x-o.x < 0 ? x-o.x+mod : x-o.x, *this;}
  modint &operator *= (modint o) {return x = 1ll*x*o.x%mod, *this;}
  modint &operator ^= (int b) {
    modint a = *this, c = 1;
    for (; b; b >>= 1, a *= a) if(b & 1) c *= a;
    return x=c.x, *this;
  }
  modint &operator /= (modint o) {return *this *= o ^= mod-2;}
  friend modint operator + (modint a, modint b) {return a += b;}
  friend modint operator - (modint a, modint b) {return a -= b;}
  friend modint operator * (modint a, modint b) {return a *= b;}
  friend modint operator / (modint a, modint b) {return a /= b;}
  friend modint operator ^(modint a,int b) {return a ^= b;}
  friend bool operator == (modint a, int b) {return a.x == b;}
  friend bool operator != (modint a, int b) {return a.x != b;}
  bool operator ! () {return !x;}
  modint operator - () {return x ? mod-x : 0;}
  bool operator < (const modint &b) const {return x < b.x;}
};
inline modint qpow(modint x, int y) {return x ^ y;}

std::vector<modint> fac, ifac, iv;
inline void initC(int n) {
  if(iv.empty()) fac = ifac = iv = std::vector<modint>(2,1);
  int m = iv.size(); ++n;
  if(m >= n) return;
  iv.resize(n + 1), fac.resize(n + 1), ifac.resize(n + 1);
  for (int i = m; i <= n; i++) {
    iv[i] = iv[mod%i] * (mod - mod/i);
    fac[i] = fac[i-1] * i, ifac[i] = ifac[i-1] * iv[i];
  }
}
inline modint C(int n, int m) {
  if(m<0 || n<m) return 0;
  return initC(n), fac[n] * ifac[m] * ifac[n-m];
}

并查集

int find(int u) {return fa[u] == u ? u : fa[u] = find(fa[u]);}
void merge(int u, int v) {
  u = find(u); v = find(v); if(u != v) fa[u] = v;
}

可撤销并查集

struct mdf {
  int x, y, dh;
};
struct dsu {
  int fa[N], h[N], siz[N];
  int find(int x) {return fa[x] == x ? x : find(fa[x]);}
  mdf q[N]; int top;
  void merge(int x, int y) {
    if((x = find(x)) != (y = find(y))) {
      if(h[x] <= h[y]) swap(x, y);
      siz[x] += siz[y]; fa[y] = x; h[x] += h[x] == h[y];
      q[++top] = {x, y, h[x] == h[y]};
    }
  }
  void roll(int x) {
    while (top > x) {
      int x = q[top].x, y = q[top].y, dh = q[top].dh;
      siz[x] -= siz[y]; fa[y] = y; h[x] -= dh; --top;
    }
  }
}t;

数论函数筛子

int mu[M], p[M], tt, phi[M], low[M], invn[M], invphi[M]; bool np[M];
void pre(int n){
    phi[1] = mu[1] = 1; low[1] = 1; invn[1] = 1;
    for(int i = 2; i <= n; i++) {
        invn[i] = 1ll * (mod - mod/i) * invn[mod % i] % mod;
        if(!np[i]) p[++tt] = i, mu[i] = -1, phi[i] = i-1, low[i] = i;
        for(int j = 1; j <= tt; j++) {
            int k = p[j] * i; if(k > n) break;
            if(i % p[j] == 0) {
                np[k] = 1; mu[k] = 0; phi[k] = phi[i] * p[j];
                low[k] = low[i] * p[j];
                break;
            }
            np[k] = 1; mu[k] = -mu[i]; phi[k] = phi[i] * (p[j]-1); low[k] = p[j];
        }
    } 
}

\(\mu\) and \(\varphi\)

int mu[M], p[M], tt, phi[M]; bool f[M]; LL summu[M], sumphi[M];
void pre(int n) {
  sumphi[1] = summu[1] = f[1] = mu[1] = 1;
  for (int i = 2; i <= n; i++) {
    if(!f[i]) p[++tt] = i, mu[i] = -1, phi[i] = i-1;
    for (int j = 1; j <= tt; j++) {
      int k = p[j] * i; if(k > n) break;
      if(i % p[j] == 0) {f[k] = 1; mu[k] = 0; phi[k] = phi[i] * p[j]; break;}
      f[k] = 1; mu[k] = -mu[i]; phi[k] = phi[i] * (p[j]-1);
    }
    summu[i] = summu[i-1] + 1ll * mu[i]; sumphi[i] = sumphi[i-1] + 1ll * phi[i];
  } 
}

dij

struct node {
  int dis, i;
  bool operator < (const node &tmp) const {
    return dis > tmp.dis;
  }
}d[M];
bool vis[M]; priority_queue<node> q;
void dij(int t) {
  for (int i = 1; i <= n; i++) d[i].dis = inf, d[i].i = i;
  d[t].dis = 0; q.push(d[t]);
  while (!q.empty()) {
    int u = q.top().i; q.pop();
    if(vis[u]) continue; vis[u] = 1;
    for (int i = head[u]; i; i = e[i].nxt) {
      int v = e[i].to;
      if(d[v].dis > d[u].dis + e[i].w) {
        d[v].dis = d[u].dis + e[i].w;
        q.push(d[v]);
      }
    }
  }
}

膜数非质数的组合数

struct num {
  num() {x = 0; memset(md, 0, sizeof(md));}
  num(int k) {
    memset(md, 0, sizeof(md)); if(k == 0) return; 
    for (int i = 1; i <= cnt; i++) {
      while (k % p[i] == 0) k /= p[i], ++md[i];
    }
    x = k; invx = inv(k);
  }
  num operator / (const num &t) const{
    num ans; ans.x = 1ll * x * t.invx % mod, ans.invx = 1ll * invx * t.x % mod;
    for (int i = 1; i <= cnt; i++) ans.md[i] = md[i] - t.md[i];
    return ans;
  }
  num operator * (const num &t) const{
    num ans; ans.x = 1ll * x * t.x % mod, ans.invx = 1ll * invx * t.invx % mod;
    for (int i = 1; i <= cnt; i++) ans.md[i] = md[i] + t.md[i];
    return ans;
  }
  int to_num() {
    int ans = x;
    for (int i = 1; i <= cnt; i++) ans = 1ll * ans * qpow(p[i], md[i]) % mod;
    return ans;
  }
  int x, invx; short md[N];
} s[M];
int C(int n, int m) {
  if(m > n || m < 0) return 0;
  return (s[n] / s[m] / s[n-m]).to_num();
}
void pre() {
  int t = mod;
  for (int i = 2; i * i <= t; i++)
    if(t % i == 0) {p[++cnt] = i; while (t % i == 0) t /= i;}
  if(t > 1) p[++cnt] = t;
  phimod = phi(mod);
  s[0] = num(1);
  for (int i = 1; i <= n; i++) s[i] = s[i-1] * num(i);
}

一棵可爱的可持久化 Trie

#define D 30
struct Trie {
  int rt[M], son[M][2], cnt[M], cnt1;
  inline int build() {return ++cnt1;}
  void ins(int &p, int q, int k, int dep) {
    if(dep == -2) return;
    cnt[p = build()] = cnt[q] + 1; son[p][0] = son[q][0]; son[p][1] = son[q][1];
    ins(son[p][(k >> dep) & 1], son[q][(k >> dep) & 1], k, dep-1);
  }
  int query(int l, int r, int k, int dep) {
    int val = (k >> dep) & 1;
    if(dep == -1) return 0;
    if(cnt[son[r][val ^ 1]] - cnt[son[l][val ^ 1]]) return (1 << dep) | query(son[l][val^1], son[r][val^1], k, dep-1);
    return query(son[l][val], son[r][val], k, dep-1);
  }
  void debug(int l, int r, int dep) {
    if(dep == -1) return;
    printf("now l=%d r=%d dep=%d cnt=%d\n", l, r, dep, cnt[r] - cnt[l]);
    if(cnt[son[r][0]] - cnt[son[l][0]]) printf("to l\n"), debug(son[l][0], son[r][0], dep-1);
    if(cnt[son[r][1]] - cnt[son[l][1]]) printf("to r\n"), debug(son[l][1], son[r][1], dep-1);
    printf("end l=%d r=%d dep=%d\n", l, r, dep);
  }
} s1, s2;

倍增 LCA

struct LCA {
  int f[M][35], d[M];
  LCA() {memset(f, 0, sizeof(f)); memset(d, 0, sizeof(d));}
  void dfs(int u, int fa) {
    f[u][0] = fa; d[u] = d[fa] + 1;
    for (int i = 1; i <= 30; i++) f[u][i] = f[f[u][i - 1]][i - 1];
    for (int i = head[u]; i; i = e[i].nxt) {
      int v = e[i].to; if(v == fa) continue; dfs(v, u);
    }
  }
  int lca(int u, int v) {
    if (d[u] < d[v]) swap(u, v);
    for (int i = 30; i >= 0; i--) if(d[f[u][i]] >= d[v]) u = f[u][i];
    if (u == v) return u;
    for (int i = 30; i >= 0; i--) if(f[u][i] != f[v][i]) u = f[u][i], v = f[v][i];
    return u == v ? u : f[u][0];
  }
}t;

Splay

struct Splay {
  #define ls(x) ch[x][0]
  #define rs(x) ch[x][1]
  #define pos(x) (ch[fa[x]][1] == x)
  int ch[M][2], val[M], cnt[M], cnt1, siz[M], fa[M], rt;
  void pushup(int x) {siz[x] = siz[ls(x)] + siz[rs(x)] + cnt[x];}
  void clear(int x) {ch[x][0] = ch[x][1] = fa[x] = val[x] = siz[x] = cnt[x] = 0;}
  int build(int t) {++cnt1; cnt[cnt1] = siz[cnt1] = 1; val[cnt1] = t; return cnt1;}
  void rotate(int x) {
    int y = fa[x], z = fa[fa[x]], p = pos(x), p2 = pos(y);
    ch[y][p] = ch[x][p^1]; if(ch[x][p^1]) fa[ch[x][p^1]] = y;
    fa[y] = x; ch[x][p^1] = y; fa[x] = z;
    if(z) ch[z][p2] = x;
    pushup(y); pushup(x);
  }
  void splay(int x) {
    for (int f = fa[x]; f = fa[x], f; rotate(x)) {
      if(fa[f]) rotate(pos(x) == pos(f) ? f : x);
    }
    rt = x;
  }
  void insert(int t) {
    if(!rt) return rt = build(t), void();
    int x = rt, y = 0;
    while (1) {
      if(val[x] == t) return ++cnt[x], pushup(x), pushup(y), splay(x), void();
      y = x; x = ch[x][val[x] < t];
      if(!x) {
        x = ch[y][val[y] < t] = build(t);
        fa[x] = y; pushup(x); pushup(y); splay(x); return;
      }
    }
  }
  int kth(int k) {
    int x = rt;
    while (1) {
      if(k <= siz[ls(x)]) {x = ls(x); continue;}
      k -= siz[ls(x)];
      if(k <= cnt[x]) return splay(x), val[x];
      k -= cnt[x]; x = rs(x);
    }
  }
  int rnk(int k) {
    int x = rt, ans = 0;
    while (1) {
      if(val[x] > k) {x = ls(x); continue;}
      ans += siz[ls(x)];
      if(k == val[x]) return splay(x), ans+1;
      ans += cnt[x]; x = rs(x);
    }
  }
  int pre() {
    int x = ls(rt); 
    while (rs(x)) x = rs(x);
    return splay(x), x;
  }
  int nxt() {
    int x = rs(rt);
    while (ls(x)) x = ls(x);
    return splay(x), x;
  }
  void del(int k) {
    rnk(k);
    if(cnt[rt] > 1) return --cnt[rt], pushup(rt), void();
    if(!ls(rt) && !rs(rt)) return clear(rt), rt = 0, void();
    if(!ls(rt)) {int r = rt; rt = rs(rt); fa[rt] = 0; clear(r); return;}
    if(!rs(rt)) {int r = rt; rt = ls(rt); fa[rt] = 0; clear(r); return;}
    int r = rt; pre(); ch[rt][1] = rs(r); fa[rs(r)] = rt; clear(r); pushup(rt); return;
  }
  int pre(int k) {insert(k); int ans = val[pre()]; del(k); return ans;}
  int nxt(int k) {insert(k); int ans = val[nxt()]; del(k); return ans;}
} tr;

经封装的线段树 & 树剖板子

int w[M], fa[M], dep[M], siz[M], son[M], top[M], dfn[M], rnk[M], out[M], cnt;
void dfs1(int u, int f) {
  fa[u] = f; siz[u] = 1; son[u] = -1;
  for (int i = head[u]; i; i = e[i].nxt) {
    int v = e[i].to; if(v == f) continue;
    dep[v] = dep[u] + 1; dfs1(v, u); siz[u] += siz[v]; 
    if(son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
  }
}
void dfs2(int u, int f, int t) {
  dfn[u] = ++cnt; rnk[cnt] = u; top[u] = t;
  if(son[u] != -1) dfs2(son[u], u, t);
  for (int i = head[u]; i; i = e[i].nxt) {
    int v = e[i].to; if(v == f || v == son[u]) continue;
    dfs2(v, u, v);
  } out[u] = cnt;
}
int n, m, r, u, v, x, y, z, op; long long p;
struct sg{
  int s[M << 2], laz[M << 2];
  void pushdown(int o, int l, int r) {
    int mid = l + r >> 1;
    s[o<<1] = (s[o<<1] + (mid-l+1) * laz[o] % p) % p; laz[o<<1] = (laz[o<<1] + laz[o]) % p;
    s[o<<1|1] = (s[o<<1|1] + (r-mid) * laz[o] % p) % p; laz[o<<1|1] = (laz[o<<1|1] + laz[o]) % p;
    laz[o] = 0;
  }
  void build(int o, int l, int r) {
    if(l == r) {s[o] = w[rnk[l]]; return;}
    int mid = l + r >> 1;
    build(o<<1, l, mid); build(o<<1|1, mid+1, r);
    s[o] = (s[o<<1] + s[o<<1|1]) % p;
  }
  void modify(int o, int l, int r, int x, int y, int t) {
    if(x <= l && r <= y) {s[o] = (s[o] + (r-l+1) * t % p) % p; laz[o] = (laz[o] + t) % p; return;}
    int mid = l + r >> 1; pushdown(o, l, r);
    if(x <= mid) modify(o<<1, l, mid, x, y, t);
    if(y > mid) modify(o<<1|1, mid+1, r, x, y, t);
    s[o] = (s[o<<1] + s[o<<1|1]) % p;
  }
  int query(int o, int l, int r, int x, int y) {
    if(x <= l && r <= y) return s[o];
    int mid = l + r >> 1, ans = 0; pushdown(o, l, r);
    if(x <= mid) ans = (ans + query(o<<1, l, mid, x, y)) % p;
    if(y > mid) ans = (ans + query(o<<1|1, mid+1, r, x, y)) % p;
    return ans;
  }
  void debug(int o, int l, int r) {
    printf("%d: l = %d  r = %d  s = %d\n", o, l, r, s[o]);
    if(l == r) return;
    int mid = l + r >> 1;
    debug(o<<1, l, mid); debug(o<<1|1, mid+1, r);
  }
}s;
void add(int x, int y, int z) {
  int tx = top[x], ty = top[y];
  while (tx != ty) {
    if(dep[tx] > dep[ty]) s.modify(1, 1, n, dfn[tx], dfn[x], z), x = fa[tx];
    else s.modify(1, 1, n, dfn[ty], dfn[y], z), y = fa[ty];
    tx = top[x]; ty = top[y];
  }
  s.modify(1, 1, n, min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), z); return;
}
int query(int x, int y) {
  int tx = top[x], ty = top[y], ans = 0;
  while (tx != ty) {
    if(dep[tx] > dep[ty]) ans = (ans + s.query(1, 1, n, dfn[tx], dfn[x])) % p, x = fa[tx];
    else ans = (ans + s.query(1, 1, n, dfn[ty], dfn[y])) % p, y = fa[ty];
    tx = top[x]; ty = top[y];
  }
  ans = (ans + s.query(1, 1, n, min(dfn[x], dfn[y]), max(dfn[x], dfn[y]))) % p; return ans;
}

sa(不确定正确性)

struct sa {
  int sa[M], rk[M << 1], cnt[M], id[M], prerk[M << 1], h[M], val[M << 1], n; char s[M];
  void init(char *l) {
    n = 1; s[1] = l[0]; 
    for (; l[n-1] != '\0'; n++) s[n] = l[n-1]; 
    --n;
  }
  bool cmp(int i, int j, int t) {return prerk[sa[j]] == prerk[sa[i]] && prerk[sa[j]+t] == prerk[sa[i]+t];}
  void get_sa() {
    int m = std::max(256, n), p = 256;
    for (int i = 1; i <= n; i++) ++cnt[rk[i] = s[i]];
    for (int i = 1; i <= m; i++) cnt[i] += cnt[i - 1];
    for (int i = n; i; i--) sa[cnt[rk[i]]--] = i;
    for (int i = 0; (1 << i) <= n; i++) {
      int t = (1 << i), k = 0; m = p; //printf("%d\n", m);
      for (int j = n; j > n - t; j--) id[++k] = j;
      for (int j = 1; j <= n; ++j)
        if (sa[j] > t) id[++k] = sa[j] - t;
      memset(cnt, 0, sizeof(cnt));
      for (int j = 1; j <= n; j++) ++cnt[val[j] = rk[id[j]]];
      for (int j = 1; j <= m; j++) cnt[j] += cnt[j-1];
      for (int j = n; j; j--) sa[cnt[val[j]]--] = id[j];
      memcpy(prerk, rk, sizeof(rk)); p = 0;
      for (int j = 1; j <= n; j++) {
        if(cmp(j, j-1, t)) rk[sa[j]] = p;
        else rk[sa[j]] = ++p;
      }
      if (p == n) break;
    }
    for (int i = 1, k = 0; i <= n; i++) {
      if (k) --k;
      while (s[i+k] == s[sa[rk[i]-1] + k]) ++k;
      h[rk[i]] = i;
    } 
  } 
  bool find(char *str) {
    int l = 1, r = n, ans = 0;
    while (l <= r) {
      int mid = l + r >> 1, val = strcmp(str, &s[sa[mid]]);
      if (val == 0) return 1;
      else if (val == -1) r = mid - 1, ans = mid;
      else l = mid + 1;
    }
    int m = strlen(str), pl = sa[ans];
    for (int i = 1; i <= m; i++) if(str[i-1] != s[pl+i]) return 0;
    return 1;
  }
} s;

轻微封装的 dinic

struct wll {
  int S, T;
  struct edge {
    int to, nxt, w;
  }e[M << 1];
  int head[M], cnt1 = 1;
  void link(int u, int v, int w) {
    e[++cnt1] = {v, head[u], w}; head[u] = cnt1;
    e[++cnt1] = {u, head[v], w}; head[v] = cnt1;
  }
  queue<int> q; int cur[M], n, m, d[M];
  bool bfs() {
    while (q.size()) q.pop();
    q.push(S);
    for (int i = 1; i <= n; i++) cur[i] = head[i], d[i] = 0;
    d[S] = 1;
    while (!q.empty()) {
      int u = q.front(); q.pop();
      for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if(e[i].w && !d[v]) {
          d[v] = d[u] + 1; q.push(v);
          if(v == T) return 1;
        }
      }
    } return 0;
  }
  int dinic(int u, int flow) {
    if(u == T) return flow;
    int ret = flow, i;
    for (i = cur[u]; i; i = e[i].nxt) {
      int v = e[i].to;
      if(e[i].w && d[v] == d[u]+1) {
        int t = dinic(v, min(ret, e[i].w));
        if(!t) d[v] = 0;
        e[i].w -= t; e[i^1].w += t;
        ret -= t;
        if(!ret) return flow;
      }
    }
    cur[u] = i;
    return flow - ret;
  }
  int work() {
    int ans = 0;
    while (bfs()) ans += dinic(S, inf);
    return ans;
  }
};

二维压一维+拆点

#define in(a, b) (((a)-1) * c + (b))
#define out(a, b) (in((a), (b)) + r*c)
void print(int u) {
  if(u == S) printf("S");
  else if(u == T) printf("T");
  else printf("(%d,%d,%d)", ((u-1)/c) % r + 1, (u-1)%c+1, u>r*c);
}

poly

反正某道题的提交记录有。

分块

分块基操:查询时需要整块内部贡献、散块对整块、整块对整块。分别处理。

然后有了一种分块:处理 \(pre_{i,j}, nxt_{i,j}\) 分别表示 \(j\) 到第 \(i\) 块块头(\(pos_j \geq i\))与 \(j\) 到第 \(i\) 块块尾(\(pos_j \leq i\))内部的贡献。附加条件是快速处理散块对散块的贡献。

流程:

  1. 处理 \(pos_j=i\) 的部分。这块可以暴力统计贡献,\(O(n \sqrt n)\)
  2. \(pre_{i,j} = pre_{i, j-1} + pre_{i+1, j} + ans(i, j)\),其中 \(ans_{i,j}\)\(i\) 整块对 \(j\) 的答案。这里的 \(+\) 意为结合。

\(ans_{i, j}\) 采取整块式的处理,即统一为所有同块的 \(j\) 求出答案。

看一下这样做的性质。

首先它是在区间选点对。暴力统计 \(pos_j=i\) 的一部分需要这个来只枚举一对点。

然后至少一个区间对一个区间的贡献要好求些。这道题用了双指针,均摊 \(O(1)\)

一个区间对一个区间很多时候好处在于能按另一个维度的信息排序。这题就是按数值大小排序。

最后信息要好结合。\(\min, +\) 都是好结合的信息。

这里处理的是区间一维最近点对。

void cmin(int &x, int y) { x = min(x, y); }
int n, m, a[M];
pair<int, int> b[M];
int len, block, L[N], R[N], pos[M];
int pre[N][M],
    nxt[N][M];  // j 到 i 块首(pos_j <= i)、j 到 i 块头(pos_j >= i)
int ans[M];
void calc(int p, int q) {  // q 块所有点对 p 块算 ans
  int pos = L[p];
  for (int i = L[q]; i <= R[q]; i++) {
    int x = b[i].first;
    while (pos < R[p] && b[pos + 1].first <= x) ++pos;
    int y = b[pos].first, z = pos + 1 == R[p] + 1 ? 2e9 : b[pos + 1].first;
    ans[b[i].second] = min(abs(x - y), abs(x - z));
  }
}
void init() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) scanf("%d", &a[i]), b[i] = {a[i], i};
  len = sqrt(n);
  block = (n - 1) / len + 1;
  for (int i = 1; i <= block; i++) {
    L[i] = R[i - 1] + 1, R[i] = min(i * len, n);
    for (int j = L[i]; j <= R[i]; j++) pos[j] = i;
  }
  for (int i = 1; i <= block; i++) {
    sort(b + L[i], b + R[i] + 1);
    pre[i][L[i]] = 2e9;
    for (int j = L[i] + 1; j <= R[i]; j++) {
      int mn = 2e9;
      for (int k = L[i]; k < j; k++) cmin(mn, abs(a[j] - a[k]));
      pre[i][j] = min(pre[i][j - 1], mn);
    }
    nxt[i][R[i]] = 2e9;
    for (int j = R[i] - 1; j >= L[i]; j--) {
      int mn = 2e9;
      for (int k = R[i]; k > j; k--) cmin(mn, abs(a[j] - a[k]));
      nxt[i][j] = min(nxt[i][j + 1], mn);
    }
  }
  for (int l = 2; l <= block; l++) {
    for (int i = 1; i + l - 1 <= block; i++) {
      int k = i + l - 1;
      calc(i, k);
      for (int j = L[k]; j <= R[k]; j++)
        pre[i][j] = min(pre[i][j - 1], min(pre[i + 1][j], ans[j]));
    }
    for (int i = block; i - l + 1 >= 1; i--) {
      int k = i - l + 1;
      calc(i, k);
      for (int j = R[k]; j >= L[k]; j--) 
        nxt[i][j] = min(nxt[i][j + 1], min(nxt[i - 1][j], ans[j]));
    }
  }
}
int tmp[N], tmp1[N], tmp2[N];
int query(int l, int r) {
  int p = pos[l], q = pos[r], res = 2e9;
  if (p == q) {
    int tot = 0;
    for (int i = L[p]; i <= R[p]; i++)
      if(l <= b[i].second && b[i].second <= r) tmp[++tot] = b[i].first;
    for (int i = 1; i < tot; i++)
      cmin(res, tmp[i + 1] - tmp[i]);
    return res;
  }
  int tot1 = 0, tot2 = 0;
  for (int i = L[p]; i <= R[p]; i++)
    if (l <= b[i].second && b[i].second <= r) tmp1[++tot1] = b[i].first;
  for (int i = L[q]; i <= R[q]; i++)
    if (l <= b[i].second && b[i].second <= r) tmp2[++tot2] = b[i].first;
  merge(tmp1 + 1, tmp1 + tot1 + 1, tmp2 + 1, tmp2 + tot2 + 1, tmp + 1);
  for (int i = 1; i < tot1 + tot2; i++)
    cmin(res, tmp[i + 1] - tmp[i]);
  return min(res, min(nxt[q - 1][l], pre[p + 1][r]));
}
void solve() {
  int m; scanf("%d", &m);
  while (m--) {
    int l, r; scanf("%d %d", &l, &r);
    printf("%d\n", query(l, r));
  }
}
int main() {
  init(), solve();
}

AC 自动机

不妨把每个前缀看作一个状态,fail 树的意义为存在的最长后缀,tr 指向加该字母后的存在的最长后缀。所有为给定串的后缀的状态处在 沿 tr 走到的最终节点 在 fail 树上的所有祖先。

\(p \in \text{subtree}(q)\),则 \(p\) 代表的状态是 \(q\) 代表的状态的后缀。

ACAM 除了能够进行字符串匹配,还常与动态规划相结合,因为它精确刻画了文本串与 所有 模式串的匹配情况。同时,\(\delta\) 函数自然地为动态规划的转移指明了方向。因此,当遇到形如 “不能出现若干单词” 的字符串 计数或最优化 问题,可以考虑在 ACAM 上 DP,将 ACAM 的状态写进 DP 的一个维度。

魏老师 orz

int tot[N], tr[N][26], cnt, fail[N];
int init() {
  ++cnt;
  memset(tr[cnt], 0, sizeof(tr[cnt]));
  fail[cnt] = tot[cnt] = 0;
  return cnt;
}
void insert(string &s) {
  int cur = 0; 
  for (int i = 0; i < s.size(); i++) {
    if (!tr[cur][s[i] - '0']) tr[cur][s[i] - '0'] = init();
    cur = tr[cur][s[i] - '0'];
  }
  ++tot[cur];
}
queue<int> q;
void build() {
  memset(fail, 0, sizeof(fail));
  for (int i = 0; i < 26; i++) if (tr[0][i]) q.push(tr[0][i]);
  while (!q.empty()) {
    int x = q.front(); q.pop(); tot[x] += tot[fail[x]];
    for (int i = 0; i < 26; i++) {
      if (tr[x][i]) fail[tr[x][i]] = tr[fail[x]][i], q.push(tr[x][i]);
      else tr[x][i] = tr[fail[x]][i];
    }
  }
}

SAM

SAM 以高度压缩的方式储存了一个串的所有子串,每个节点是一个 endpos 等价类集合,长度连续。

SAM 接受给定字符串的所有后缀。

fa 指向的是该等价类最短节点长度再减一对应节点所在等价类。

a[p].len - a[a[p].fa].len 即为该等价类的节点个数。

可以线段树合并维护 endpos 集合。空间多开一倍就行。

沿着 fa 树往下走的过程是往后加字符而等价类分裂的过程。fa 树叶子按 dfn 序排列构成 SA。

SAM 的 fa 树是反串后缀树。

struct node {
  int t[26], f, len;
} a[M << 1];
int las = 1, cnt = 1;
vector<int> e[M << 1];
int w[M << 1];
void ins(int c) {
  int p = las, np = ++cnt; las = np; a[np].len = a[p].len + 1;
  w[np] = 1;
  for (; p && !a[p].t[c]; p = a[p].f) a[p].t[c] = np;
  if(!p) return a[np].f = 1, void();
  int q = a[p].t[c];
  if(a[q].len == a[p].len + 1) return a[np].f = q, void();
  else {
    int nq = ++cnt; a[nq] = a[q]; a[nq].len = a[p].len + 1;
    a[np].f = a[q].f = nq;
    for (; p && a[p].t[c] == q; p = a[p].f) a[p].t[c] = nq;
  }
}
void pre() {
  for (int i = 2; i <= cnt; i++) e[a[i].f].push_back(i);
}
LL ans = 0;
void dfs(int u) {
  for (auto v : e[u]) dfs(v), w[u] += w[v];
  // do something.
}
posted @ 2022-09-29 20:40  purplevine  阅读(126)  评论(0编辑  收藏  举报