海亮 7.10 树论

Hailiang 7.10 树论/树上问题

树上问题:

  1. 树形\(dp\) 技巧:在状态里面加上父亲/儿子/祖先的状态
  2. 重心,直径的性质
  3. 链问题:树上差分/树剖/\(LCA\)(树上倍增)
  4. 树上路径统计问题:淀粉质/动态淀粉质/淀粉树
  5. 基环树

Trick:

  1. 求某个点到根节点的权值和

    \(DFS\)时维护数组 进点时++ 回溯时-- 在进点时查询即可

  2. 求某个子树权值和

    \(DFS\) 进的时候把当前权值挂到节点上(比如说颜色信息\(c\)值 开一个新数组记录下来这个状态)

    \(dfs\)子树并更新权值 那么我们在出去的时候将现在的新权值和老权值相减即为答案

P5838 [USACO19DEC] Milk Visits G

一道比较板的树剖题 显然将询问离线 每次加入一种颜色并回答这个颜色的所有询问

开始以为用\(vector\)暴力维护插入的点会\(T\) 所以打了一个\(build\)函数+区间修改线段树 但是挂了

最后改用\(vector\)过的()

代码一:

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mkp make_pair
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define int long long
const int N = 2e5 + 5;
const int inf = 9e18;

inline int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N] , test , testcol , ans[N]; 

vector<int> v[N];

int head[N] , cnt;
struct node { int to , nxt; } e[N<<1];
void add ( int u , int v ) { e[++cnt] = { v , head[u] } , head[u] = cnt; }

struct que { int x , y , id , col , ans; } q[N];

int son[N] , fa[N] , sz[N] , dep[N];
int pos[N] , top[N] , rev[N] , timer;

void dfs1 ( int u , int f )
{
	fa[u] = f , sz[u] = 1 , dep[u] = dep[f] + 1;
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( v == f ) continue;
		dfs1 ( v , u );
		sz[u] += sz[v];
		if ( sz[son[u]] < sz[v] ) son[u] = v;
	}
}

void dfs2 ( int u , int tp )
{
	top[u] = tp , pos[u] = ++timer , rev[timer] = pos[u];
	if ( son[u] ) dfs2 ( son[u] , tp );
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( fa[u] == v || son[u] == v ) continue;
		dfs2 ( v , v );
	}
}

struct kk { int cov , val; } t[N<<2];

void up ( int p ) { t[p].val = max ( t[ls].val , t[rs].val ); }

void upd ( int p , int l , int r , int x , int val )
{
	if ( l == r ) return t[p].val = val , void();
	if ( x <= mid ) upd ( lson , x , val );
	else upd ( rson , x , val );
	up(p);
} 

int query ( int p , int l , int r , int x , int y )
{
	if ( x <= l && r <= y ) return t[p].val;
	int res = -inf;
	if ( x <= mid ) res = max ( res , query ( lson , x , y ) );
	if ( mid + 1 <= y ) res = max ( res , query ( rson , x , y ) );
	return res;	
}

int querymax ( int u , int v )
{
	int res = -inf;
	while ( top[u] != top[v] )
	{
		if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
		res = max ( res , query ( 1 , 1 , n , pos[top[u]] , pos[u] ) );
//		cout << "test:" << test << " col=" << testcol << " top=" << pos[top[u]] << " bottom=" << pos[u] << " ans=" << query ( 1 , 1 , n , pos[top[u]] , pos[u] ) << endl;
		u = fa[top[u]];
	} 
	if ( dep[u] < dep[v] ) swap ( u , v );
	res = max ( res , query ( 1 , 1 , n , pos[v] , pos[u] ) );
//	cout << "test:" << test << " col=" << testcol << " top=" << pos[v] << " bottom=" << pos[u] << " ans=" << query ( 1 , 1 , n , pos[v] , pos[u] ) << endl;
	return res > 0 ? 1 : 0;	
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , v[a[i]].push_back(i);
	for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
	dfs1 ( 1 , 0 ) , dfs2 ( 1 , 1 );
	for ( int i = 1 ; i <= m ; i ++ ) q[i].x = read() , q[i].y = read() , q[i].col = read() , q[i].id = i;
//	for ( int i = 1 ; i <= n ; i ++ ) cout << pos[top[i]] << ' ' << pos[i] << endl;
	sort ( q + 1 , q + m + 1 , [](const que a , const que b) { return a.col < b.col; } );
	int now = 1;
	for ( int i = 1 ; i <= q[m].col ; i ++ )
	{
		for ( int j = 0 ; j < v[i].size() ; j ++ ) upd ( 1 , 1 , n , pos[v[i][j]] , i );
		while ( q[now].col == i ) ans[q[now].id] = querymax ( q[now].x , q[now].y ) , now ++;
		for ( int j = 0 ; j < v[i].size() ; j ++ ) upd ( 1 , 1 , n , pos[v[i][j]] , 0 );
	}		
	for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i];
	return 0;
}


代码二:(主席树版本)

比较好理解的在线做法 树剖求\(lca\) 主席树维护这个点到根节点的颜色 最后查询用差分查询即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mkp make_pair
#define mid ((l+r)>>1)
#define ls(p) t[p].son[0]
#define rs(p) t[p].son[1]
#define lson ls(p),l,mid
#define rson rs(p),mid+1,r
#define int long long
const int N = 2e5 + 5;
const int inf = 9e18;

inline int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , a[N] , test , testcol , ans[N] , root[N]; 

int head[N] , cnt;
struct node { int to , nxt; } e[N<<1];
void add ( int u , int v ) { e[++cnt] = { v , head[u] } , head[u] = cnt; }

int son[N] , fa[N] , sz[N] , dep[N];
int pos[N] , top[N] , rev[N] , timer;

struct tree
{
	void dfs1 ( int u , int f )
	{
		fa[u] = f , sz[u] = 1 , dep[u] = dep[f] + 1;
		for ( int i = head[u] ; i ; i = e[i].nxt )
		{
			int v = e[i].to;
			if ( v == f ) continue;
			dfs1 ( v , u );
			sz[u] += sz[v];
			if ( sz[son[u]] < sz[v] ) son[u] = v;
		}
	}
	void dfs2 ( int u , int tp )
	{
		top[u] = tp , pos[u] = ++timer , rev[timer] = pos[u];
		if ( son[u] ) dfs2 ( son[u] , tp );
		for ( int i = head[u] ; i ; i = e[i].nxt )
		{
			int v = e[i].to;
			if ( fa[u] == v || son[u] == v ) continue;
			dfs2 ( v , v );
		}
	}
	int lca ( int u , int v )
	{
		while ( top[u] != top[v] )
		{
			if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
			u = fa[top[u]];
		} 
		if ( dep[u] < dep[v] ) swap ( u , v );
		return v;
	}
}L;
struct DQY 
{
	struct kk { int son[2] , val; } t[N<<5];
	int tot = 0;
	int new_node ( int p ) { t[++tot] = t[p]; return tot; }
	void upd ( int &p , int l , int r , int x , int val )
	{
		p = new_node(p);
		t[p].val ++;
		if ( l == r ) return;
		if ( x <= mid ) upd ( lson , x , val );
		else upd ( rson , x , val );
	}
	int query ( int u , int v , int l , int r , int col )
	{
		if ( l == r ) return t[v].val - t[u].val;
		if ( col <= mid ) return query ( ls(u) , ls(v) , l , mid , col );
		else return query ( rs(u) , rs(v) , mid + 1 , r , col );
	}
}T;

void dfs ( int u , int f )
{
	root[u] = root[f];
	T.upd ( root[u] , 1 , n , a[u] , 1 );
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( v == f ) continue;
		dfs ( v , u );
	}
} 

int query ( int x , int y , int col )
{
	int f = L.lca ( x , y );
	int ff = fa[f];
	int res = 0;
	res += T.query ( root[ff] , root[x] , 1 , n , col );
	res += T.query ( root[f] , root[y] , 1 , n , col );
	return res > 0 ? 1 : 0;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
	L.dfs1 ( 1 , 0 ) , L.dfs2 ( 1 , 1 );
	dfs ( 1 , 0 );
	for ( int i = 1 ; i <= m ; i ++ )
	{
		int x = read() , y = read() , col = read();
		cout << query ( x , y , col );
	}
	return 0;
}

Trees of Tranquillity

很明显我们第一棵树上的点都应该是一条链上的节点

所以我们\(dfs\)第一棵树 同时维护第二棵树上的信息

那么对于第二棵树 节点之间不应该存在子树关系 又因为子树区间在\(dfn\)序上是连续的 那么策略就是:

如果第一个区间被第二个区间包含 那么选择更小的区间

首先一个\(DFS\)处理每个节点在第二棵树中的区间 再\(DFS\)第一棵树

必须注意回溯时的区间置0操作顺序

\(upd\):袁神给出了一组\(hack\) 但是题目中有父亲节点的编号小于子节点的编号的性质 这样可以保证父亲节点在第二棵树上维护的\(dfn\)序列一定大于子节点 \(hack\)未成功

感谢他的贡献让我对题目加深了理解()

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mkp make_pair
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair

const int N = 3e5 + 5;
const int inf = 2e9;

inline int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m;

int st[N] , ed[N] , timer , sum , ans;

int head1[N] , head2[N] , cnt;
struct node { int to , nxt; } e[N<<2];
void add1 ( int u , int v ) { e[++cnt] = { v , head1[u] } , head1[u] = cnt; }
void add2 ( int u , int v ) { e[++cnt] = { v , head2[u] } , head2[u] = cnt; }

struct DQY
{
	struct node { int tag , val; } t[N<<2];
	void up ( int p ) { t[p].val = max ( t[ls].val , t[rs].val ); }
	void changedown ( int p , int val ) { t[p].tag = t[p].val = val; }
	void down ( int p ) { if ( t[p].tag != -1 ) changedown ( ls , t[p].tag ) , changedown ( rs , t[p].tag ) , t[p].tag = -1; }
	void build ( int p , int l , int r )
	{
		t[p].tag = -1 , t[p].val = 0;
		if ( l == r ) return;
		build ( lson ) , build ( rson );
	}
	void upd ( int p , int l , int r , int x , int y , int val )
	{
		if ( x <= l && r <= y ) return changedown ( p , val ) , void();
		down(p);
		if ( x <= mid ) upd ( lson , x , y , val );
		if ( mid + 1 <= y ) upd ( rson , x , y , val );
		up(p);
	}
	int query ( int p , int l , int r , int x , int y ) 
	{
		if ( x <= l && r <= y ) return t[p].val;
		down(p); int res = -inf;
		if ( x <= mid ) res = max ( res , query ( lson , x , y ) );
		if ( mid + 1 <= y ) res = max ( res , query ( rson , x , y ) );
		return res;
	}
}T;


void dfs2 ( int u )
{
	st[u] = ++timer;
	for ( int i = head2[u] ; i ; i = e[i].nxt ) dfs2(e[i].to);
	ed[u] = timer;
}
void dfs1 ( int u )
{
	int tt = T.query ( 1 , 1 , n , st[u] , ed[u] );
	if ( tt ) T.upd ( 1 , 1 , n , st[tt] , ed[tt] , 0 ) , T.upd ( 1 , 1 , n , st[u] , ed[u] , u );
	else sum ++ , T.upd ( 1 , 1 , n , st[u] , ed[u] , u );
	ans = max ( ans , sum );
	for ( int i = head1[u] ; i ; i = e[i].nxt ) dfs1(e[i].to);
	if ( tt ) T.upd ( 1 , 1 , n , st[u] , ed[u] , 0 ) , T.upd ( 1 , 1 , n , st[tt] , ed[tt] , tt );//这里的更新顺序必须注意 否则置0操作会将赋值tt操作顶掉 
	else sum -- , T.upd ( 1 , 1 , n , st[u] , ed[u] , 0 );
}

void solve()
{
	memset ( head1 , 0 , sizeof head1 ) , memset ( head2 , 0 , sizeof head2 ) , cnt = 0;
	n = read();
	for ( int i = 2 , fa ; i <= n ; i ++ ) fa = read() , add1 ( fa , i );
	for ( int i = 2 , fa ; i <= n ; i ++ ) fa = read() , add2 ( fa , i );
	timer = 0 , dfs2 ( 1 );
	ans = sum = 0 , dfs1 ( 1 );
	cout << ans << endl;
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	int T = read();
	while ( T -- ) solve();
	return 0;
}

P2491 [SDOI2011] 消防

一个性质:如果一个树网有多个直径 那么必然交于直径的中点

\(O(n^3)\)做法显然 枚举两个直径上的端点 为这一段打上标记 并枚举这一段上的每一个点\(DFS\)即可

一个优化:对于一个端点\(p\) \(q\)必然贪心地取最远点 那么省掉了\(i\)这一维度 时间复杂度\(O(n^2)\)

\(O(n)\)做法:还是枚举\(p\) 但是我们对于每一对\(p,q\)处理(\(O(1)\))

离这个树网的核最远的点只可能是 直径两端的点到\(p,q\)的最远距离 或者是其他点到直径的最远距离

因为对于\(p,q\),从它们出发最远的点一定是直径两端的点 不存在其他点 否则就不是直径了

对于取点可以用单调队列\(O(n)\)维护

打了两遍 乐 第一遍抄太多了

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mkp make_pair
#define mid ((l+r)>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair

const int N = 6e5 + 5;
const int inf = 2e9;

inline int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , s , s1 , s2 , vis[N] , dis[N] , fa[N] , maxs , maxdis , ans = inf;

int head[N] , cnt;
struct node { int to , nxt , w; } e[N<<2];
void add ( int u , int v , int w ) { e[++cnt] = { v , head[u] , w }; head[u] = cnt; }

void dfs ( int u , int f )
{
	fa[u] = f;
	if ( dis[u] > dis[s2] ) s2 = u;
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( v == f ) continue;
		dis[v] = dis[u] + e[i].w;
		dfs ( v , u );
	}
}

void mark_len()//标记直径 顺便统计点到直径一个端点的距离 
{
	memset ( dis , 0 , sizeof dis );//初始值设置为0 
	int u = s2; vis[u] = 1;
	while ( u != s1 ) 
	{
		for ( int i = head[u] ; i ; i = e[i].nxt )
		{
			int v = e[i].to;
			if ( v == fa[u] ) dis[v] = dis[u] + e[i].w , vis[v] = 1;
		}
		u = fa[u];
	}
} 

int find ( int u , int s )//u向右s步能走到的最远节点 
{
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( v == fa[u] && s >= e[i].w ) return find ( v , s - e[i].w );
	}
	return u;	
}

void dis_len()//枚举一个起点 处理直径两端点到这条链的距离最大值的最小值
{
	int u = s2;
	while ( u != s1 ) ans = min ( ans , max ( dis[u] , dis[s1] - dis[find(u,s)] ) ) , u = fa[u];
	ans = min ( ans , max ( dis[u] , dis[s1] - dis[find(u,s)] ) ); 
} 

void query ( int u , int f )
{
	for ( int i = head[u] ; i ; i = e[i].nxt )
	{
		int v = e[i].to;
		if ( vis[v] || v == f ) continue;
		dis[v] = dis[u] + e[i].w; 
		query ( v , u );		
	}
}

void node_len ()//处理每一个点到直径的距离 
{
	memset ( dis , 0 , sizeof dis );
	int u = s2;
	while ( u != s1 ) query ( u , 0 ) , u = fa[u];
	query ( u , 0 );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , s = read();	
	for ( int i = 1 , u , v , w ; i < n ; i ++ ) u = read() , v = read() , w = read() , add ( u , v , w ) , add ( v , u , w );
	dfs(1,0) , s1 = s2 , s2 = 0 , dis[s1] = 0;
	dfs(s1,0);
	mark_len();
	dis_len();
	node_len();
	for ( int i = 1 ; i <= n ; i ++ ) ans = max ( ans , dis[i] );
	cout << ans << endl;
	return 0;
}

P1600 [NOIP2016 提高组] 天天爱跑步

天天将会臭名昭著!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

我们首先考虑让天天换一个爱好

考虑对于每一个观察员在满足什么条件的时候会收到贡献

每一次询问 都可以拆成两条链(\(u->lca\)\(lca->v\))

考虑第一条链

对于观察员\(i\) 如果它的子树中有节点满足\(dep[u]=dep[i]+a[i]\) 那么满足条件

考虑第二条链

可以推出 如果\(dep[i]-a[i]=dep[v]-dis(u,v)\) 那么会产生贡献

\(dep[i]-a[i]\)有可能小于\(0\) 那么我们对两边都加上\(n\)即可(因为题目中限制了\(w[i]\le n\))

所以 在上行途中 符合条件的起点可以做贡献 下行中 符合条件的终点可以做出贡献

那么我们开一个桶\(cnt\) 里面装着贡献 那么我们\(dfs\)一遍子树 差值就是这个节点的贡献

特别要注意:

在统计当前结点作为起点和终点所产生的贡献 继而计算出当前结点作为“根”上的差值后

在回溯过程中 一定要减去以当前结点为LCA的起点终点在桶里产生的贡献 这部分贡献在离开这个子树后就没有意义了 因为如果不删去的话 它上面的节点会错误地接收到这些路径的贡献

#include <bits/stdc++.h>
using namespace std;
#define int long long 
const int N = 3e5 + 5;
inline int read ()
{
	int x = 0 , f = 1;
	char ch = cin.get();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = cin.get(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = cin.get(); }
	return x * f;
}

int n , m , cnt1[N<<1] , cnt2[N<<1] , a[N] , ans[N];

vector<int> q1[N] , q2[N] , q3[N] , q4[N];

int head[N] , cnt;
struct node { int to , nxt; } e[N<<1];
void add ( int u , int v ) { e[++cnt] = { v , head[u] } , head[u] = cnt; }

int fa[N] , dep[N] , son[N] , sz[N] , top[N];

struct DQY
{
	void dfs1 ( int u , int f )
	{
		dep[u] = dep[fa[u]=f] + 1 , sz[u] = 1;
		for ( int i = head[u] , v ; v = e[i].to , i ; i = e[i].nxt )
			if ( v != f )
			{
				dfs1 ( v , u ) , sz[u] += sz[v];
				if ( sz[son[u]] < sz[v] ) son[u] = v;
			}
	}
	void dfs2 ( int u , int tp )
	{
		top[u] = tp;
		if ( son[u] ) dfs2 ( son[u] , tp );
		for ( int i = head[u] , v ; v = e[i].to , i ; i = e[i].nxt )
			if ( v != son[u] && v != fa[u] ) dfs2 ( v , v );
	}
	int lca ( int u , int v )
	{
		while ( top[u] != top[v] )
		{
			if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
			u = fa[top[u]];	
		}
		if ( dep[u] < dep[v] ) swap ( u , v );
		return v;
	}	
}L;

struct query { int u , v , lca , dis; } q[N];

void dfs1 ( int u )//搜dep[u]=dep[i]+a[i]
{
	int now = dep[u] + a[u] , cur = cnt1[now];
	for ( int i = head[u] , v ; v = e[i].to , i ; i = e[i].nxt ) if ( v != fa[u] ) dfs1(v);
	for ( int i = 0 ; i < q1[u].size() ; i ++ ) ++cnt1[q1[u][i]]; 
	ans[u] += cnt1[now] - cur;//当前节点的答案计入 
	for ( int i = 0 ; i < q2[u].size() ; i ++ ) --cnt1[q2[u][i]];//将这棵子树中的所有点都清理掉 
}

void dfs2 ( int u )//搜dep[v]-dis(u,v)+n=dep[i]-a[i]+n
{
	int now = dep[u] - a[u] + n , cur = cnt2[now];
	for ( int i = head[u] , v ; v = e[i].to , i ; i = e[i].nxt ) if ( v != fa[u] ) dfs2(v);
	for ( int i = 0 ; i < q3[u].size() ; i ++ ) ++cnt2[q3[u][i]]; 
	for ( int i = 0 ; i < q4[u].size() ; i ++ ) --cnt2[q4[u][i]]; //相当于我们忽略掉了lca这个节点 
	ans[u] += cnt2[now] - cur;
}


signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
	L.dfs1(1,0) , L.dfs2(1,1);
	for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
	for ( int i = 1 ; i <= m ; i ++ ) q[i].u = read() , q[i].v = read() , q[i].lca = L.lca ( q[i].u , q[i].v ) , q[i].dis = dep[q[i].u] + dep[q[i].v] - 2 * dep[q[i].lca];
	for ( int i = 1 ; i <= m ; i ++ ) q1[q[i].u].push_back(dep[q[i].u]) , q2[q[i].lca].push_back(dep[q[i].u]);
	dfs1(1); //搜dep[u]=dep[i]+a[i]
	for ( int i = 1 ; i <= m ; i ++ ) q3[q[i].v].push_back(dep[q[i].v]-q[i].dis+n) , q4[q[i].lca].push_back(dep[q[i].v]-q[i].dis+n);
	dfs2(1); //搜dep[v]-dis(u,v)+n=dep[i]-a[i]+n
	for ( int i = 1 ; i <= n ; i ++ ) cout << ans[i] << ' ';	
	return 0;
}

posted @ 2023-07-11 07:43  Echo_Long  阅读(12)  评论(1编辑  收藏  举报