(mathcal{Description})
Link.
给定一棵含 (n) 个结点的树,结点 (1) 为根,点 (u) 初始有点权 (a_u=0),维护 (q) 次操作:
- 给定 (u),将 (u) 子树内的点权加 (1);
- 给定 (u,v),将 (u,v) 简单路径上的点权加 (1)。
每次操作后,求出树最靠近结点 (1) 的带权重心。
(n,qle10^5)。
(mathcal{Solution})
记点权和为 (S),发现这么一个事情:
从 (1) 开始任意 DFS 树,在经过点权和不小于 (lceilfrac{S}{2} ceil) 时停止在结点 (x),则带权重心为 (x) 及其祖先中的某一个。
当然转化在 DFN 序列上更简洁
DFN 序列的前缀和恰好超过 (lceilfrac{S}{2} ceil) 的位置就是上述 (x) 的 DFN。
证明上,这样的遍历把树分为一个点权和不小于 (lceilfrac{S}{2} ceil) 的连通块 (A) 和若干连通块 (B)。注意带权重心的基本性质是以重心为根时,不存在一条连向点权和大于 (lfloorfrac{S}{2} floor) 的子树。首先考虑 (B) 中的结点 (u),若 (u) 能成为重心,其父亲必然也能成为重心,而且其父亲更靠近 (1),所以 (u) 不优;对于 (A) 中不是 (x) 祖先的结点同理,所以证明了这条性质。
回到本题,难点已经扫除了。树剖维护点权,线段树二分找到 (x),倍增祖先找到重心即可。复杂度 (mathcal O(qlog^2n))。
(mathcal{Code})
/*~Rainybunny~*/
#include <cstdio>
#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
typedef long long LL;
const int MAXN = 1e5, MAXLG = 16;
int n, ecnt, head[MAXN + 5], ans = 1;
struct Edge { int to, nxt; } graph[MAXN * 2 + 5];
int fa[MAXN + 5][MAXLG + 5], siz[MAXN + 5], son[MAXN + 5], dep[MAXN + 5];
int dfc, dfn[MAXN + 5], top[MAXN + 5], ref[MAXN + 5];
inline void link( const int u, const int v ) {
graph[++ecnt] = { v, head[u] }, head[u] = ecnt;
graph[++ecnt] = { u, head[v] }, head[v] = ecnt;
}
inline void init( const int u ) {
dep[u] = dep[fa[u][0]] + 1, siz[u] = 1;
for ( int i = 1; fa[u][i - 1]; fa[u][i] = fa[fa[u][i - 1]][i - 1], ++i );
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) != fa[u][0] ) {
fa[v][0] = u, init( v ), siz[u] += siz[v];
if ( siz[v] > siz[son[u]] ) son[u] = v;
}
}
}
inline void init( const int u, const int tp ) {
top[u] = tp, ref[dfn[u] = ++dfc] = u;
if ( son[u] ) init( son[u], tp );
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) != fa[u][0] && v != son[u] ) {
init( v, v );
}
}
}
struct SegmentTree {
int len[MAXN << 2];
LL sum[MAXN << 2], tag[MAXN << 2];
inline void pushad( const int u, const LL v ) {
sum[u] += len[u] * v, tag[u] += v;
}
inline void pushup( const int u ) {
sum[u] = sum[u << 1] + sum[u << 1 | 1];
}
inline void pushdn( const int u ) {
if ( tag[u] ) {
pushad( u << 1, tag[u] ), pushad( u << 1 | 1, tag[u] );
tag[u] = 0;
}
}
inline void init( const int u, const int l, const int r ) {
len[u] = r - l + 1;
if ( l == r ) return ;
int mid = l + r >> 1;
init( u << 1, l, mid ), init( u << 1 | 1, mid + 1, r );
}
inline void add( const int u, const int l, const int r,
const int al, const int ar, const int v ) {
if ( al <= l && r <= ar ) return pushad( u, v );
int mid = l + r >> 1; pushdn( u );
if ( al <= mid ) add( u << 1, l, mid, al, ar, v );
if ( mid < ar ) add( u << 1 | 1, mid + 1, r, al, ar, v );
pushup( u );
}
inline LL qsum( const int u, const int l, const int r,
const int ql, const int qr ) {
if ( ql <= l && r <= qr ) return sum[u];
int mid = l + r >> 1; LL ret = 0; pushdn( u );
if ( ql <= mid ) ret += qsum( u << 1, l, mid, ql, qr );
if ( mid < qr ) ret += qsum( u << 1 | 1, mid + 1, r, ql, qr );
return ret;
}
inline int qhalf( const int u, const int l, const int r, const LL h ) {
if ( l == r ) return l;
int mid = l + r >> 1; pushdn( u );
if ( sum[u << 1] >= h ) return qhalf( u << 1, l, mid, h );
else return qhalf( u << 1 | 1, mid + 1, r, h - sum[u << 1] );
}
} sgt;
inline void subtrAdd( const int u ) {
sgt.add( 1, 1, n, dfn[u], dfn[u] + siz[u] - 1, 1 );
}
inline void chainAdd( int u, int v ) {
while ( top[u] != top[v] ) {
if ( dep[top[u]] < dep[top[v]] ) u ^= v ^= u ^= v;
sgt.add( 1, 1, n, dfn[top[u]], dfn[u], 1 ), u = fa[top[u]][0];
}
if ( dep[u] < dep[v] ) u ^= v ^= u ^= v;
sgt.add( 1, 1, n, dfn[v], dfn[u], 1 );
}
inline void solve() {
int u = ref[sgt.qhalf( 1, 1, n, sgt.sum[1] + 1 >> 1 )];
per ( i, MAXLG, 0 ) {
int v = fa[u][i];
if ( v && sgt.qsum( 1, 1, n, dfn[v],
dfn[v] + siz[v] - 1 ) << 1 <= sgt.sum[1] ) {
u = fa[v][0];
}
}
if ( fa[u][0] && sgt.qsum( 1, 1, n, dfn[u],
dfn[u] + siz[u] - 1 ) << 1 <= sgt.sum[1] ) u = fa[u][0];
printf( "%d
", u );
}
int main() {
scanf( "%d", &n );
rep ( i, 2, n ) {
int u, v; scanf( "%d %d", &u, &v );
link( u, v );
}
init( 1 ), init( 1, 1 ), sgt.init( 1, 1, n );
int q, op, u, v; scanf( "%d", &q );
while ( q-- ) {
scanf( "%d %d", &op, &u );
if ( op == 1 ) subtrAdd( u );
else scanf( "%d", &v ), chainAdd( u, v );
solve();
}
return 0;
}