模板
数据结构
线段树2
void build(int p, int l, int r) {
l(p) = l, r(p) = r;
if(l == r) return ;
int mid = l+r >> 1;
build(ls(p) = p<<1, l, mid), build(rs(p) = p<<1|1, mid+1, r);
}
void pushdown(int p) {
sum(ls(p)) = (sum(ls(p)) * mul(p) + add(p) * (r(ls(p)) - l(ls(p))+1)) % mo;
sum(rs(p)) = (sum(rs(p)) * mul(p) + add(p) * (r(rs(p)) - l(rs(p))+1)) % mo;
sum(ls(p)) %= mo, sum(rs(p)) %= mo;
add(ls(p)) = add(ls(p)) * mul(p) + add(p), add(rs(p)) = add(rs(p)) * mul(p) + add(p);
add(ls(p)) %= mo, add(rs(p)) %= mo;
mul(ls(p)) = mul(ls(p)) * mul(p) % mo, mul(rs(p)) = mul(rs(p)) * mul(p) % mo;
add(p) = 0, mul(p) = 1;
}
void changeadd(int p, int l, int r, ll s) {
if(l(p) >= l && r(p) <= r) {
sum(p) += s * (r(p) - l(p)+1);
add(p) += s;
sum(p) %= mo, add(p) %= mo;
return ;
}
pushdown(p);
int mid = l(p)+r(p) >> 1;
if(l <= mid) changeadd(ls(p), l, r, s);
if(r > mid) changeadd(rs(p), l, r, s);
sum(p) = (sum(ls(p)) + sum(rs(p))) % mo;
}
void changemul(int p, int l, int r, ll s) {
if(l(p) >= l && r(p) <= r) {
sum(p) = sum(p) * s % mo;
add(p) = add(p) * s % mo;
mul(p) = mul(p) * s % mo;
return ;
}
pushdown(p);
int mid = l(p)+r(p) >> 1;
if(l <= mid) changemul(ls(p), l, r, s);
if(r > mid) changemul(rs(p), l, r, s);
sum(p) = (sum(ls(p)) + sum(rs(p))) % mo;
}
ll ask(int p, int l, int r) {
if(l(p) >= l && r(p) <= r) return sum(p);
pushdown(p);
int mid = l(p)+r(p) >> 1;
ll res = 0;
if(l <= mid) res += ask(ls(p), l, r);
if(r > mid) res += ask(rs(p), l, r);
return res % mo;
}
主席树
int n, m, tot, cnt, a[N], num[N], rt[N];
struct tree {
int ls, rs, sum;
#define ls(p) t[p].ls
#define rs(p) t[p].rs
#define sum(p) t[p].sum
} t[N];
int get(int x) {
return lower_bound(num+1, num+cnt+1, x) - num;
}
// int build(int l, int r) { // 初始形态
// int p = ++tot;
// if(l == r) return p;
// int mid = l+r>>1;
// ls(p) = build(l, mid);
// rs(p) = build(mid+1, r);
// return p;
// }
void insert(int root, int &p, int k, int l, int r) { // 返回根
p = ++tot;
ls(p) = ls(root), rs(p) = rs(root), sum(p) = sum(root)+1;
if(l == r) return ;
int mid = l+r>>1;
if(k <= mid) insert(ls(root), ls(p), k, l, mid);
else insert(rs(root), rs(p), k, mid+1, r);
}
int ask(int x, int y, int k, int l, int r) {
if(l == r) return l;
int mid = l+r>>1, s = sum(ls(y)) - sum(ls(x));
if(s < k) return ask(rs(x), rs(y), k - s, mid+1, r);
return ask(ls(x), ls(y), k, l, mid);
}
扫描线 (线段/面积并)
namespace sgt {
#define ls(p) p<<1
#define rs(p) p<<1|1
int len[N<<2], tag[N<<2];
void pushup(int p, int a, int b) {
len[p] = a == b ? 0 : len[ls(p)] + len[rs(p)];
}
void modify(int p, int l, int r, int x, int y, int k) {
if(l >= x && r <= y) {
tag[p] += k;
if(tag[p]) len[p] = ::x[r+1] - ::x[l];
else pushup(p, l, r);
return ;
}
int mid = l+r>>1;
if(x <= mid) modify(ls(p), l, mid, x, y, k);
if(y > mid) modify(rs(p), mid+1, r, x, y, k);
if(!tag[p]) pushup(p, l, r);
}
}
using sgt::modify;
using sgt::len;
线段树合并
struct tree {
// 权值线段树: 维护值域(颜色)[l,r]区间的信息(此处为个数)
int rt, ls, rs, res, sum;
#define rt(p) t[p].rt
#define res(p) t[p].res
#define sum(p) t[p].sum
#define ls(p) t[p].ls
#define rs(p) t[p].rs
} t[N+3<<7];
void update(int p) {
if(sum(ls(p)) >= sum(rs(p))) sum(p) = sum(ls(p)), res(p) = res(ls(p));
else sum(p) = sum(rs(p)), res(p) = res(rs(p));
}
int merge(int a, int b, int l, int r) {
if(!a) return b;
if(!b) return a;
if(l == r) {
sum(a) += sum(b);
return a;
}
int mid = l+r>>1;
ls(a) = merge(ls(a), ls(b), l, mid);
rs(a) = merge(rs(a), rs(b), mid+1, r);
update(a);
return a;
}
int tot;
int add(int id, int l, int r, int col, int val) {
if(!id) id = ++tot;
if(l == r) {
res(id) = col;
sum(id) += val;
return id;
}
int mid = l+r>>1;
if(col <= mid) ls(id) = add(ls(id), l, mid, col, val);
else rs(id) = add(rs(id), mid+1, r, col, val);
update(id);
return id;
}
李超线段树
void update(int p, int l, int r, int k) {
if(l > tr[p].r || r < tr[p].l) return ;
if(l <= tr[p].l && r >= tr[p].r) {
if(check(k, tr[p].dat, tr[p].l) && check(k, tr[p].dat, tr[p].r)) return ; // worse
if(check(tr[p].dat, k, tr[p].l) && check(tr[p].dat, k, tr[p].r)) {
tr[p].dat = k;
return ; // better
}
int mid = tr[p].l+tr[p].r >> 1;
if(check(tr[p].dat, k, mid)) swap(tr[p].dat, k);
if(check(tr[p].dat, k, tr[p].l)) update(tr[p].ls, tr[p].l, tr[p].r, k);
else update(tr[p].rs, tr[p].l, tr[p].r, k);
}
else update(tr[p].ls, l, r, k), update(tr[p].rs, l, r, k);
}
int query(int p, int k) {
if(k < tr[p].l || k > tr[p].r) return 0;
if(tr[p].l == tr[p].r && tr[p].l == k) return tr[p].dat;
int mid = tr[p].l+tr[p].r >> 1, res;
if(k <= mid) res = query(tr[p].ls, k);
else res = query(tr[p].rs, k);
if(check(res, tr[p].dat, k)) res = tr[p].dat;
return res;
}
Splay
int n, m, fa[N], son[N][2], val[N], cnt[N], sz[N];
// 父亲 儿子 值 个数 子树大小
int rt, tot;
struct Splay {
void clear(int x) {fa[x]=son[x][0]=son[x][1]=val[x]=cnt[x]=sz[x] = 0;}
void update(int x) {sz[x] = sz[son[x][0]] + sz[son[x][1]] + cnt[x];}
bool get(int x) {return x == son[fa[x]][1];} // 左右儿子
void rotate(int x) {
int y = fa[x], z = fa[y], k = get(x);
son[y][k] = son[x][k^1];
if(son[x][k^1]) fa[son[x][k^1]] = y;
son[x][k^1] = y;
fa[y] = x, fa[x] = z;
if(z) son[z][y == son[z][1]] = x;
update(y), update(x); // 注意顺序
}
void splay(int x) {
for(int f; f = fa[x]; rotate(x)) {
if(fa[f]) rotate(get(x) == get(f) ? f:x);
}
rt = x;
}
int rnk(int k) {
ins(x);
int res = 0, cur = rt;
while(1) {
if(k < val[cur]) cur = son[cur][0];
else {
res += sz[son[cur][0]];
if(val[cur] == k) {
splay(cur);
return res+1;
}
res += cnt[cur], cur = son[cur][1];
}
}
del(x);
}
int kth(int k) {
int cur = rt;
while(1) {
if(son[cur][0] && k <= sz[son[cur][0]]) cur = son[cur][0];
else {
k -= cnt[cur] + sz[son[cur][0]];
if(k <= 0) {
splay(cur);
return val[cur];
}
cur = son[cur][1];
}
}
}
void ins(int k) {
if(!rt) {
val[rt = ++tot] = k, cnt[tot]++;
update(rt);
return ;
}
int cur = rt, f=0;
while(cur) {
if(val[cur] == k) {
cnt[cur]++;
update(cur), update(f);
splay(cur);
return ;
}
f = cur, cur = son[cur][val[cur] < k];
}
val[++tot] = k, cnt[tot]++;
fa[tot] = f, son[f][val[f] < k] = tot;
update(tot), update(f);
splay(tot);
}
int pre() {
int cur = son[rt][0];
if(!cur) return cur;
while(son[cur][1]) cur = son[cur][1];
splay(cur);
return cur;
}
int suf() {
int cur = son[rt][1];
if(!cur) return cur;
while(son[cur][0]) cur = son[cur][0];
splay(cur);
return cur;
}
void del(int k) {
rnk(k); // 转到根节点
if(cnt[rt]-- > 1) {
update(rt);
return ;
}
if(!son[rt][0] && !son[rt][1]) {
clear(rt);
rt = 0;
return ;
}
int cur = rt;
if(!son[rt][0]) {
rt = son[rt][1], fa[rt] = 0;
clear(cur);
return ;
}
if(!son[rt][1]) {
rt = son[rt][0], fa[rt] = 0;
clear(cur);
return ;
}
int x = pre();
fa[son[cur][1]] = x, son[x][1] = son[cur][1];
clear(cur), update(rt);
}
} tree;
2D-BIT
struct BIT {
#define lb(i) i&-i
int a[N][N], ai[N][N], aj[N][N], aij[N][N];
void add(int i, int j, int x) {
for(int ii=i; ii <= n; ii += lb(ii)) {
for(int jj=j; jj <= m; jj += lb(jj)) {
a[ii][jj] += x;
ai[ii][jj] += x * i;
aj[ii][jj] += x * j;
aij[ii][jj] += x * i*j;
}
}
}
int ask(int i, int j) {
int s=0;
for(int ii=i; ii; ii -= lb(ii)) {
for(int jj=j; jj; jj -= lb(jj)) {
s += aij[ii][jj];
s -= aj[ii][jj] * (i+1);
s -= ai[ii][jj] * (j+1);
s += a[ii][jj] * (i+1)*(j+1);
}
}
return s;
}
#define y1 yone
void add(int x1, int y1, int x2, int y2, int v) {
add(x1, y1, v);
add(x1, y2+1, -v);
add(x2+1, y1, -v);
add(x2+1, y2+1, v);
}
int ask(int x1, int y1, int x2, int y2) {
int s=0;
s += ask(x1-1, y1-1);
s -= ask(x1-1, y2);
s -= ask(x2, y1-1);
s += ask(x2, y2);
return s;
}
}
回滚莫队
rep(j, 1, bn) {
int br = min(j*len, n), l = br+1, r = l-1, cnt=0, res=0;
for(; b[q[i].l] == j; ++i) {
if(b[q[i].r] == j) {ans[q[i].i] = calc(q[i].l, q[i].r); continue;} // bf
while(r < q[i].r) {
++r;
lst[a[r]] = r;
if(!fst[a[r]]) fst[a[r]]=r, del[++cnt] = a[r];
res = max(res, r - fst[a[r]]);
}
int tmp = res;
while(l > q[i].l) {
--l;
if(lst[a[l]]) res = max(res, lst[a[l]] - l);
else lst[a[l]] = l;
}
ans[q[i].i] = res;
while(l <= br) {
if(lst[a[l]] == l) lst[a[l]] = 0;
++l;
}
res = tmp;
}
rep(i, 1, cnt) fst[del[i]] = lst[del[i]] = 0;
}
01 Trie(Xor Mst)
int n, rt;
struct trie {
int a[N], ch[2][N<<5], cnt, l[N<<5], r[N<<5];
void ins(int &k, int id, int dep) {
if(!k) k = ++cnt;
if(!~dep) return ;
if(!l[k]) l[k] = id;
r[k] = id;
ins(ch[a[id]>>dep&1][k], id, dep-1);
}
ll ask(int k, int x, int dep) {
if(!~dep) return 0;
int b = x>>dep&1;
if(ch[b][k]) return ask(ch[b][k], x, dep-1);
return ask(ch[b^1][k], x, dep-1) + (1<<dep);
}
ll dfs(int k, int dep) {
if(!~dep) return 0;
if(ch[0][k] && ch[1][k]) {
ll res = INF;
rep(i, l[ch[0][k]], r[ch[0][k]]) res = min(res, ask(ch[1][k], a[i], dep-1) + (1<<dep));
return res + dfs(ch[0][k], dep-1) + dfs(ch[1][k], dep-1);
}
if(ch[0][k]) return dfs(ch[0][k], dep-1);
if(ch[1][k]) return dfs(ch[1][k], dep-1);
return 0;
}
void solve() {
in(a, n);
sort(a+1, a+n+1);
rep(i, 1, n) ins(rt, i, 30);
wr(dfs(rt, 30));
}
} tr;
可持久化并查集
字符串
KMP
void kmp() {
int j=0;
rep(i, 2, n) {
while(j && s[i] != s[j+1]) j = nxt[j];
if(s[i] == s[j+1]) ++j;
nxt[i] = j;
}
}
manacher
void manacher() {
scanf("%s", str);
n = strlen(str);
rep(i, 0, n) {
s[i<<1|1] = '#';
s[i+1<<1] = str[i];
}
n = n<<1|1;
s[0] = '~';
// rep(i, 0, n) pc(s[i]);
int mxr=0, p=0;
rep(i, 1, n) {
if(i < mxr) f[i] = min(f[2*p-i], mxr-i);
else f[i] = 1;
while(s[i+f[i]] == s[i-f[i]]) f[i]++;
if(i+f[i] > mxr) mxr = i+f[i], p=i;
ans = max(ans, f[i]-1);
}
}
AC Automaton
struct ACAM {
int nxt[30][N], fail[N], ed[N], cnt[N], tot;
void clear() {
mem(nxt, 0);
mem(fail, 0);
mem(ed, 0);
mem(cnt, 0);
tot = 0;
}
void insert(char *s, int id) {
int n = strlen(s), p=0;
rep(i, 0, n-1) {
int k = s[i]-'a';
if(!nxt[k][p]) nxt[k][p] = ++tot;
p = nxt[k][p];
}
ed[p] = id;
}
void getfail() {
queue <int> q;
rep(i, 0, 25) if(nxt[i][0]) q.push(nxt[i][0]);
while(!q.empty()) {
int u = q.front();
q.pop();
rep(i, 0, 25) {
if(!nxt[i][u]) nxt[i][u] = nxt[i][fail[u]];
else q.push(nxt[i][u]), fail[nxt[i][u]] = nxt[i][fail[u]];
}
}
}
void ask(char *s) {
int n = strlen(s), p=0, res=0;
rep(i, 0, n-1) {
int k = s[i]-'a';
p = nxt[k][p];
for(int j=p; j; j=fail[j]) cnt[ed[j]]++;
}
}
} acam;
Z Algorithm
void exkmp(string s) {
n = s.size();
for(i = 1; i < n; ++i) {
if(j+z[j] > i) z[i] = min(z[i-j], j+z[j]-i);
while(s[z[i]] == s[i+z[i]]) ++z[i];
if(j+z[j] < i+z[i]) j=i;
}
}
Minshow
int minshow() {
int i=0, j=1, k=0;
while(i<n && j<n && k<n) {
if(a[(i+k)%n] == a[(j+k)%n]) k++;
else {
if(a[(i+k)%n] > a[(j+k)%n]) i += k+1;
else j += k+1;
if(i == j) j++;
k = 0;
}
}
return min(i, j);
}
SA
// 数组记得开两倍
void suffix_sort() {
int m = 128;
rep(i, 1, n) cnt[rk[i] = s[i]]++;
rep(i, 1, m) cnt[i] += cnt[i-1];
per(i, n, 1) sa[cnt[rk[i]]--] = i;
for(int k=1, p=0; p != n; k <<= 1, m = p) {
int tot = 0;
rep(i, n-k+1, n) id[++tot] = i;
rep(i, 1, n) if(sa[i] > k) id[++tot] = sa[i]-k;
mem(cnt, 0);
rep(i, 1, n) cnt[rk[i]]++;
rep(i, 1, m) cnt[i] += cnt[i-1];
per(i, n, 1) sa[cnt[rk[id[i]]]--] = id[i];
p = 0;
memcpy(lstrk, rk, sizeof(lstrk));
rep(i, 1, n) {
if(lstrk[sa[i]] == lstrk[sa[i-1]] && lstrk[sa[i]+k] == lstrk[sa[i-1]+k]) rk[sa[i]] = p;
else rk[sa[i]] = ++p;
}
}
}
图论
Tarjan
割点 and 点双
桥 and 边双
缩点:
边双 \(\to\) 树
强连通 \(\to\) DAG
点双 \(\to\) 圆方树
边双
void tarjan(int u, int cur) {
dfn[u] = low[u] = ++cnt;
for(int i = h[u]; i; i = e[i].n) {
int v = e[i].v;
if(!dfn[v]) {
tarjan(v, i);
if(dfn[u] < low[v]) bridge[i] = bridge[i^1] = 1;
low[u] = min(low[u], low[v]);
}
else if(i != (cur ^ 1)) low[u] = min(low[u], dfn[v]);
}
}
void dfs(int u, int id) {
edcc[u] = id;
v[id].pb(u);
for(int i = h[u]; i; i = e[i].n) {
int v = e[i].v;
if(edcc[v] || bridge[i]) continue;
dfs(v, id);
}
}
强连通
void tarjan(int u) {
dfn[u]=low[u] = ++t;
sta[++top] = u, vis[u] = 1;
for(int i = h[u]; i; i = e[i].n) {
int v = e[i].v;
if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]) {
sum++;
do {
scc[u] = sum;
vis[u = sta[top--]] = 0;
S[sum].push_back(u);
} while(low[u] != dfn[u]);
}
}
点双
void tarjan(int u, int fa) {
dfn[u]=low[u] = ++cnt;
st[++top] = u;
int son=0;
for(int i=h[u]; i; i=e[i].n) {
int v=e[i].v;
if(v == fa) continue;
if(!dfn[v]) {
son++;
tarjan(v, u);
low[u] = min(low[u], low[v]);
if(low[v] >= dfn[u]) {
++vdcc;
while(st[top+1] != v) ans[vdcc].pb(st[top--]);
ans[vdcc].pb(u);
}
}
else low[u] = min(low[u], dfn[v]);
}
if(!fa && !son) ans[++vdcc].pb(u);
}
最大流
int bfs() {
mem(vis, 0);
queue <int> q;
q.push(S);
vis[S] = 1;
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i=h[u]; i; i=e[i].n) {
int v=e[i].v, w=e[i].w;
if(!vis[v] && w) vis[v] = vis[u]+1, q.push(v);
}
}
return vis[T];
}
ll dfs(int u, int d) {
if(u == T) return d;
ll s=0;
for(int i=h[u]; i; i=e[i].n) {
int v=e[i].v, w = min(e[i].w, d);
if(vis[v] == vis[u]+1 && w) {
int k = dfs(v, w);
s += k, d -= k;
e[i].w -= k, e[i^1].w += k;
}
}
return s ? s : vis[u]=0;
}
ll dinic() {
ll ans = 0;
while(bfs()) {
ans += dfs(S, INF);
}
return ans;
}
费用流
void bfs() {
queue <int> q;
q.push(S);
vis[S] = INF, dis[S] = 0;
while(!q.empty()) {
int u = q.front();
q.pop();
use[u] = 0;
for(int i=h[u]; i; i=e[i].n) {
int v=e[i].v, w=e[i].w, c=e[i].c;
if(dis[u]+c < dis[v] && w) {
vis[v] = min(vis[u], w);
pre[v] = i, dis[v] = dis[u]+c;
if(!use[v]) q.push(v), use[v] = 1;
}
}
}
for(int i=pre[T]; i; i=pre[e[i].u]) {
e[i].w -= vis[T];
e[i^1].w += vis[T];
}
}
void mcmf() {
while(1) {
mem(vis, 0);
mem(dis, INF);
mem(pre, 0);
mem(use, 0);
bfs();
if(vis[T]) maxflow += vis[T], mincost += vis[T]*dis[T];
else return ;
}
}
二分图最大匹配
int dfs(int u) {
if(vis[u]) return 0;
vis[u] = 1;
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
if(!p[v] || dfs(p[v])) {
p[v] = u;
return 1;
}
}
return 0;
}
图的匹配
点分治
namespace TreeDivide {
void getrt(int u, int fa) {
int sz = 0;
size[u] = 1;
for(int i=h[u]; i; i=e[i].n) {
int v = e[i].v;
if(v == fa || vis[v]) continue;
getrt(v, u);
size[u] += size[v];
sz = max(sz, size[v]);
}
sz = max(sz, sum - size[u]);
if(sz < mnsz) rt=u, mnsz=sz;
}
void getdis(int u, int fa) {
d[++cnt] = dis[u];
for(int i=h[u]; i; i=e[i].n) {
int v = e[i].v, w = e[i].w;
if(v == fa || vis[v]) continue;
dis[v] = dis[u]+w;
getdis(v, u);
}
}
void solve(int u) {
// printf("# %d\n", u);
vis[u]=fl[0] = 1;
vector <int> tmp;
for(int i=h[u]; i; i=e[i].n) {
int v = e[i].v, w = e[i].w;
if(vis[v]) continue;
cnt=0, dis[v] = w;
getdis(v, u);
rep(j, 1, cnt) rep(k, 1, m) if(q[k] >= d[j]) ans[k] |= fl[q[k]-d[j]];
rep(j, 1, cnt) if(d[j] < V) tmp.pb(d[j]), fl[d[j]] = 1;
}
for(int x : tmp) fl[x]=0;
for(int i=h[u]; i; i=e[i].n) {
int v = e[i].v;
if(vis[v]) continue;
mnsz = INF, sum = size[v];
getrt(v, u);
solve(rt);
}
}
}
using TreeDivide::getrt;
using TreeDivide::solve;
重链剖分 (hld)
int sz[N], dep[N], son[N], fa[N], id[N], cnt, p[N], tp[N];
namespace hld {
void dfs1(int u, int f) {
dep[u] = dep[f]+1, sz[u]=1, fa[u]=f;
for(int i=h[u]; i; i=e[i].n) {
int v = e[i].v;
if(v == f) continue;
dfs1(v, u);
if(sz[v] > sz[son[u]]) son[u]=v;
sz[u] += sz[v];
}
}
void dfs2(int u, int top) {
id[u] = ++cnt, p[cnt] = u, tp[u] = top;
if(!son[u]) return ;
dfs2(son[u], top);
for(int i=h[u]; i; i=e[i].n) {
int v = e[i].v;
if(v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
struct sgt {
ll t[N<<2], tag[N<<2];
#define ls(p) p<<1
#define rs(p) p<<1|1
void pushup(int p) {
t[p] = (t[ls(p)]+t[rs(p)])%mo;
}
void pushdown(int p, int len) {
if(!tag[p]) return ;
(t[ls(p)] += tag[p]*(len+1>>1)%mo) %= mo, (t[rs(p)] += tag[p]*(len>>1)%mo) %= mo;
(tag[ls(p)] += tag[p]) %= mo, (tag[rs(p)] += tag[p]) %= mo;
tag[p]=0;
}
void add(int p, int l, int r, int x, int y, int k) {
if(l >= x && r <= y) {
t[p] += k * (r-l+1)%mo, tag[p] += k;
return ;
}
pushdown(p, r-l+1);
int mid = l+r>>1;
if(x <= mid) add(ls(p), l, mid, x, y, k);
if(y > mid) add(rs(p), mid+1, r, x, y, k);
pushup(p);
}
ll ask(int p, int l, int r, int x, int y) {
if(l >= x && r <= y) return t[p];
pushdown(p, r-l+1);
int mid = l+r>>1; ll s=0;
if(x <= mid) s += ask(ls(p), l, mid, x, y);
if(y > mid) s += ask(rs(p), mid+1, r, x, y);
return s%mo;
}
} tr;
void add(int x, int y, int v) {
while(tp[x] != tp[y]) {
if(dep[tp[x]] < dep[tp[y]]) swap(x, y);
tr.add(1, 1, n, id[tp[x]], id[x], v);
x = fa[tp[x]];
}
if(dep[x] > dep[y]) swap(x, y);
tr.add(1, 1, n, id[x], id[y], v);
}
ll ask(int x, int y) {
ll s=0;
while(tp[x] != tp[y]) {
if(dep[tp[x]] < dep[tp[y]]) swap(x, y);
s += tr.ask(1, 1, n, id[tp[x]], id[x]);
x = fa[tp[x]];
}
if(dep[x] > dep[y]) swap(x, y);
(s += tr.ask(1, 1, n, id[x], id[y])) %= mo;
return s;
}
}
using hld::dfs1;
using hld::dfs2;
using hld::add;
using hld::ask;
using hld::tr;

浙公网安备 33010602011771号