ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Solution -「Gym 102759I」Query On A Tree 17

2021-08-16 08:01:03  阅读:174  来源: 互联网

标签:head 17 int graph Gym Tree fa MAXN siz


\(\mathcal{Description}\)

  Link.

  给定一棵含 \(n\) 个结点的树,结点 \(1\) 为根,点 \(u\) 初始有点权 \(a_u=0\),维护 \(q\) 次操作:

  1. 给定 \(u\),将 \(u\) 子树内的点权加 \(1\);
  2. 给定 \(u,v\),将 \(u,v\) 简单路径上的点权加 \(1\)。

  每次操作后,求出树最靠近结点 \(1\) 的带权重心。

  \(n,q\le10^5\)。

\(\mathcal{Solution}\)

  记点权和为 \(S\),发现这么一个事情:

从 \(1\) 开始任意 DFS 树,在经过点权和不小于 \(\lceil\frac{S}{2}\rceil\) 时停止在结点 \(x\),则带权重心为 \(x\) 及其祖先中的某一个。

当然转化在 DFN 序列上更简洁

DFN 序列的前缀和恰好超过 \(\lceil\frac{S}{2}\rceil\) 的位置就是上述 \(x\) 的 DFN。

  证明上,这样的遍历把树分为一个点权和不小于 \(\lceil\frac{S}{2}\rceil\) 的连通块 \(A\) 和若干连通块 \(B\)。注意带权重心的基本性质是以重心为根时,不存在一条连向点权和大于 \(\lfloor\frac{S}{2}\rfloor\) 的子树。首先考虑 \(B\) 中的结点 \(u\),若 \(u\) 能成为重心,其父亲必然也能成为重心,而且其父亲更靠近 \(1\),所以 \(u\) 不优;对于 \(A\) 中不是 \(x\) 祖先的结点同理,所以证明了这条性质。

  回到本题,难点已经扫除了。树剖维护点权,线段树二分找到 \(x\),倍增祖先找到重心即可。复杂度 \(\mathcal O(q\log^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\n", 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;
}

标签:head,17,int,graph,Gym,Tree,fa,MAXN,siz
来源: https://www.cnblogs.com/rainybunny/p/15145950.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有