线段树合并

线段树合并

实际上全称为"动态开点权值线段树合并"

可以用来维护两棵动态开点权值线段树的合并问题

P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并

板子题 用到了一点差分的思想

我们对于每一个点开一棵以种类为下标的权值线段树 上面维护的是区间内种类最多的值

那么对于树上点的差分 若想修改(u,v)这个路径为x 那么只需要将u点和v点线段树的x位置加1 lca(u,v)fa(lca(u,v))x位置减1即可

最后做一遍树上前缀和统计即可 对于子树的合并直接线段树合并即可

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#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 eb emplace_back
constexpr int N = 1e5 + 5;
constexpr int maxn = 1e5;

char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get() 
int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}
int n , m , ans[N] , root[N];

vector<int> e[N];
void add ( int u , int v ) { e[u].eb(v); }
int fa[N] , sz[N] , dep[N] , son[N] , top[N];

struct DQY
{
	void dfs1 ( int u , int f )
	{
		dep[u] = dep[f] + 1 , fa[u] = f , sz[u] = 1;
		for ( auto v : e[u] )
		{
			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;
		if ( son[u] ) dfs2 ( son[u] , tp );
		for ( auto v : e[u] ) if ( v != fa[u] && v != son[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 segtree
{
	int tot = 0;
	struct node { int son[2] , maxx , pos; } t[N<<6];
	int new_node ( int p ) { t[++tot] = t[p]; return tot; }
	void up ( int p )
	{
		if ( t[ls(p)].maxx < t[rs(p)].maxx ) t[p].maxx = t[rs(p)].maxx , t[p].pos = t[rs(p)].pos;
		else t[p].maxx = t[ls(p)].maxx , t[p].pos = t[ls(p)].pos;
	}
	void merge ( int &p , int u , int v , int l , int r )
	{
		if ( !u || !v ) return p = u + v , void();
		if ( l == r ) return t[p].maxx = t[u].maxx + t[v].maxx , void();
		merge ( ls(p) , ls(u) , ls(v) , l , mid );
		merge ( rs(p) , rs(u) , rs(v) , mid + 1 , r );
		up(p);
	}
	void upd ( int &p , int l , int r , int x , int val )
	{
		if ( !p ) p = new_node(p); 
		if ( l == r ) return t[p].maxx += val , t[p].pos = x , void();
		if ( x <= mid ) upd ( lson , x , val );
		else upd ( rson , x , val );
		up(p);
	}
	void dfs ( int u )
	{
		for ( auto v : e[u] )
		{
			if ( v == fa[u] ) continue;
			dfs(v);
			merge ( root[u] , root[u] , root[v] , 1 , maxn );
		}
		ans[u] = t[root[u]].maxx != 0 ? t[root[u]].pos : 0;
	}
}T;



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 , u , v , x ; i <= m ; i ++ )
	{
		u = read() , v = read() , x = read();
		int f = L.lca ( u , v ) , ff = fa[f];
		T.upd ( root[u] , 1 , maxn , x , 1 ) , T.upd ( root[v] , 1 , maxn , x , 1 );
		T.upd ( root[f] , 1 , maxn , x , -1 ) , T.upd ( root[ff] , 1 , maxn , x , -1 );
	}
	T.dfs(1);
	for ( int i = 1 ; i <= n ; i ++ ) cout << ans[i] << endl;
	return 0;
}

P3224 [HNOI2012] 永无乡

基本未看题解写出来的题 过编即AC 开香槟!

区间k小值 显然想到以重要度为下标的动态开点线段树

用并查集维护连通块 动态开点线段树维护连通块内权值

查询时直接线段树上二分即可

因为要求岛的编号 那么我们还需要维护一个重要度对应的id信息

注意初始化时每一个连通块的线段树都要初始化 并且fa[i]=i

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#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 eb emplace_back
constexpr int N = 1e5 + 5;
constexpr int maxn = 1e5;

//char buf[1<<24] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get() 
int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}
int n , m , q , ans[N] , root[N] , fa[N];
int find ( int x ) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

struct segtree
{
	int tot = 0;
	struct node { int son[2] , sum , id; } t[N<<6];
	int new_node() { return ++tot; }
	void up ( int p ) { t[p].sum = t[ls(p)].sum + t[rs(p)].sum; }
	void upd ( int &p , int l , int r , int x , int val )
	{
		if ( !p ) p = new_node();
		t[p].sum ++;
		if ( l == r ) return t[p].id = val , void();
		if ( x <= mid ) upd ( lson , x , val );
		else upd ( rson , x , val );
	}
	int query ( int p , int l , int r , int k )
	{
		if ( t[p].sum < k ) return -1;
		if ( l == r ) return t[p].id;
		if ( k <= t[ls(p)].sum ) return query ( lson , k );
		else return query ( rson , k - t[ls(p)].sum );
	}
	void merge ( int &p , int u , int v , int l , int r )
	{
		if ( !u || !v ) return p = u + v , void();
		if ( l == r ) return t[p].id = t[u].id + t[v].id , t[p].sum = t[u].sum + t[v].sum , void();
		merge ( ls(p) , ls(u) , ls(v) , l , mid );
		merge ( rs(p) , rs(u) , rs(v) , mid + 1 , r );
		up(p);
	}
}T;


char ch;
signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read() , m = read();
	for ( int i = 1 ; i <= n ; i ++ ) fa[i] = i , T.upd ( root[i] , 1 , n , read() , i );
	for ( int i = 1 , u , v ; i <= m ; i ++ )
	{
		u = read() , v = read();
		int fu = find(u) , fv = find(v);
		if ( fu != fv ) fa[fv] = fu , T.merge ( root[fu] , root[fu] , root[fv] , 1 , n );
	}
	q = read();
	for ( int i = 1 , x , y ; i <= q ; i ++ )
	{
		cin >> ch; x = read() , y = read();
		if ( ch == 'Q' )
		{
			int fx = find(x);
			cout << T.query ( root[fx] , 1 , n , y ) << endl;
		}
		else 
		{
			int fx = find(x) , fy = find(y);
			fa[fy] = fx;
			T.merge ( root[fx] , root[fx] , root[fy] , 1 , n );
		}
	}
	return 0;
}

P3521 [POI2011] ROT-Tree Rotations

对于任意一个节点 交换左右子树对当前节点和前面的节点都没有影响

依照经典树上统计问题 对于一个节点 有三种逆序对

  • 在左子树中
  • 在右子树中
  • 以当前节点作为lca的逆序对

因为我们已经搞完左子树的贡献和右子树的贡献 所以我们只需要考虑第三种情况即可

显而易见地 交换子树只会对第三种情况有影响 因此我们只需要在合并线段树的同时 统计交换子树的逆序对个数u和不交换子树的逆序对个数vmin(u,v)计入答案即可

需要注意的是 线段树合并时&p如果是0的话会出锅 需要先行赋值

个人理解是 如果x0的话 那么可能有很多次merge后的答案都指向0节点 导致出锅

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#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 eb emplace_back
#define int long long 
constexpr int N = 2e5 + 5;
//char buf[1<<24] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get() 
int read ()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit ( ch ) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit ( ch ) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f;
}
int n , ans1 , ans2 , ans , rt;

struct DQY//你是我的答案=v= 
{
	int tot = 0;
	struct node { int son[2] , sz; } t[N<<5];
	int new_node() { return ++tot; }
	void up ( int p ) { t[p].sz = t[ls(p)].sz + t[rs(p)].sz; }
	
	void upd ( int &p , int l , int r , int x )
	{
		if ( !p ) p = new_node();
		t[p].sz ++;
		if ( l == r ) return;
		if ( x <= mid ) upd ( lson , x );
		else upd ( rson , x );
		up(p);
	}
	
	void merge ( int &p , int l , int r , int u , int v )
	{
		if ( !u || !v ) return p = u + v , void();
		if ( l == r ) return t[p].sz = t[u].sz + t[v].sz , void();
		ans1 += t[rs(u)].sz * t[ls(v)].sz , ans2 += t[ls(u)].sz * t[rs(v)].sz;
		merge ( lson , ls(u) , ls(v) ) , merge ( rson , rs(u) , rs(v) );
//		cout << "ans1+=" << t[rs(u)].sz << '*' << t[ls(v)].sz << endl;
//		cout << "ans2+=" << t[ls(u)].sz << '*' << t[rs(v)].sz << endl;
		up(p);
	}
	
	void dfs ( int &x )
	{
		int u = 0 , v = 0 , val = read();
		if ( !val )
		{
			dfs(u) , dfs(v);
			ans1 = 0 , ans2 = 0;//每次清空 ans1表示不交换 ans2表示交换
			merge ( x = u , 1 , n , u , v );
//			cout << ans1 << ' ' << ans2 << endl;
			ans += min ( ans1 , ans2 );
		}
		else upd ( x , 1 , n , val );
	}
}T;


signed main ()
{
//	freopen ( "a.in" , "r" , stdin );
	ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
	n = read();
	T.dfs(rt);
	cout << ans << endl;
	return 0;
}

P3605 [USACO17JAN] Promotion Counting P

线段树合并裸题 离散化一下 对于每一个点直接在权值线段树上二分统计即可

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#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 eb emplace_back
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 2e3 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , a[N] , rt[N] , ans[N] , sz;

struct segtree
{
	struct node { int son[2] , sum; } t[N<<5];
	int tot = 0;
	inl void up ( int p ) { t[p].sum = t[ls(p)].sum + t[rs(p)].sum; }
	void upd ( int &p , int l , int r , int x )
	{
		if ( !p ) p = ++tot; t[p].sum ++;
		if ( l == r ) return;
		if ( x <= mid ) upd ( lson , x );
		else upd ( rson , x );
	}
	void merge ( int &p , int l , int r , int u , int v )
	{
		if ( !u || !v ) return p = u + v , void();
		if ( l == r ) return t[p].sum = t[u].sum + t[v].sum , void();
		merge ( lson , ls(u) , ls(v) ) , merge ( rson , rs(u) , rs(v) );
		up(p);
	}
	int query ( int p , int l , int r , int k )
	{
		if ( l == r ) return t[p].sum - 1;
		if ( k <= mid ) return t[rs(p)].sum + query ( lson , k );
		else return query ( rson , k ); 
	}
}T;

vector<int>lsh,e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
	
void dfs ( int u , int fa )
{
	for ( auto v : e[u] ) if ( v != fa ) dfs ( v , u ) , T.merge ( rt[u] , 1 , sz , rt[u] , rt[v] );
	ans[u] = T.query ( rt[u] , 1 , n , a[u] );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read();
	for ( int i = 1 ; i <= n ; i ++ ) lsh.eb(a[i]=read());
	sort ( lsh.begin() , lsh.end() );
	sz = unique ( lsh.begin() , lsh.end() ) - lsh.begin();
	lsh.resize(sz);
	for ( int i = 1 ; i <= n ; i ++ )
	{
		a[i] = lower_bound ( lsh.begin() , lsh.end() , a[i] ) - lsh.begin() + 1;
		T.upd ( rt[i] , 1 , sz , a[i] );
	}
	for ( int i = 2 , fa ; i <= n ; i ++ ) fa = read() , add ( fa , i ); 
	dfs ( 1 , 0 );
	for ( int i = 1 ; i <= n ; i ++ ) cout << ans[i] << endl;
	return 0;
}

Blood Cousins

线段树合并写法 还有Dsu on tree写法(在另一篇blog中) 这里的k级祖先用了树剖来写

将询问离线 对每次询问 在k祖先上将这个询问挂起来 用权值线段树维护子树信息 向上合并信息的时候用线段树合并

u节点作为下属节点的k级祖先的时候 在线段树上查询对应的val值的个数即可

最后 因为输出的是表亲的个数 那么我们要将ans1 对于没有k级祖先的情况 设置ans=1 也就是本身 然后在最后输出答案的时候统一1

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#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 eb emplace_back
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , rt[N] , ans[N];

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

struct que { int val , id; };
vector<que> q[N];

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

struct LCA
{
	void dfs1 ( int u , int f )
	{
		dep[u] = dep[f] + 1 , fa[u] = f , sz[u] = 1;
		for ( auto v : e[u] )
			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 , pos[u] = ++timer , rev[timer] = u;
		if ( son[u] ) dfs2 ( son[u] , tp );
		for ( auto v : e[u] ) if ( v != fa[u] && v != son[u] ) dfs2 ( v , v );
	}
	int jump ( int u , int k )
	{
		while ( u != 0 && k > 0 )
		{
			if ( pos[u] - pos[top[u]] + 1 > k ) return rev[pos[u]-k];
			k -= pos[u] - pos[top[u]] + 1;
			u = fa[top[u]];
		}
		return u;
	}
}L;

struct segtree
{
	struct node { int son[2] , sum; } t[N<<5];
	int tot = 0;
	inl void up ( int p ) { t[p].sum = t[ls(p)].sum + t[rs(p)].sum; }
	void upd ( int &p , int l , int r , int x )
	{
		if ( !p ) p = ++tot; t[p].sum ++;
		if ( l == r ) return;
		if ( x <= mid ) upd ( lson , x );
		else upd ( rson , x );
	}
	void merge ( int &p , int l , int r , int u , int v )
	{
		if ( !u || !v ) return p = u + v , void();
		if ( l == r ) return t[p].sum = t[u].sum + t[v].sum , void();
		merge ( lson , ls(u) , ls(v) ) , merge ( rson , rs(u) , rs(v) );
		up(p);
	}
	int query ( int p , int l , int r , int val )
	{
		if ( l == r ) return t[p].sum;
		if ( val <= mid ) return query ( lson , val );
		else return query ( rson , val );
	}
}T;


void dfs ( int u , int f )
{
	T.upd ( rt[u] , 1 , n , dep[u] );
	for ( auto v : e[u] ) if ( v ^ f ) dfs ( v , u ) , T.merge ( rt[u] , 1 , n , rt[u] , rt[v] );
	for ( auto [val,id] : q[u] ) ans[id] = T.query ( rt[u] , 1 , n , val );
}

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read();
	for ( int i = 1 , fa ; i <= n ; i ++ ) fa = read() , add ( fa , i ) , add ( i , fa );
	for ( auto v : e[0] ) L.dfs1 ( v , 0 );
	for ( auto v : e[0] ) L.dfs2 ( v , v );
	m = read();
	for ( int i = 1 , x , y ; i <= m ; i ++ )
	{
		x = read() , y = read();
		int lca = L.jump ( x , y );
		if ( lca ) q[lca].push_back({dep[x],i});
		else ans[i] = 1;
	}
	for ( auto v : e[0] ) dfs ( v , 0 );
	for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] - 1 << ' ';
	return 0;
}

Dominant Indices

一眼线段树合并 节点以dep作为下标插入 线段树合并所有子节点 维护最大值的同时维护最大值的dep即可

采用了空间回收的写法 需要边插入点边合并 而不能将所有点都插入之后再合并 这就需要提前给root赋值(upd:不能在merge函数中新开节点 会RE)

(实际上不采用空间回收也可以过 朴素线段树合并+按照题解的空间开即可)

改了一下线段树合并的马蜂 感觉更清晰一点 注意线段树合并的时候 如果!u||!v 那么此时的v不能回收 因为有可能是0

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#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 eb emplace_back
const int inf = 0x3f3f3f3f;
const int N = 1e6 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
// #define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , ans[N] , dep[N] , rt[N];

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

struct seg 
{
	struct node { int son[2] , maxdep , maxx; void clear() { son[0] = son[1] = maxdep = maxx = 0; } } t[N<<2];
	int tot = 0 , top = 0 , sta[N<<2];
	inl int new_node() { if ( top ) return sta[top--]; return ++tot; }
	inl void recycle ( int p ) { t[p].clear() , sta[++top] = p; }
	inl void up ( int p ) { if ( t[ls(p)].maxx >= t[rs(p)].maxx ) t[p].maxx = t[ls(p)].maxx , t[p].maxdep = t[ls(p)].maxdep; else t[p].maxx = t[rs(p)].maxx , t[p].maxdep = t[rs(p)].maxdep; }
	void upd ( int &p , int l , int r , int x )
	{
		if ( !p ) p = new_node(); 
		if ( l == r ) return t[p].maxx ++ , t[p].maxdep = l , void();
		if ( x <= mid ) upd ( lson , x );
		else upd ( rson , x );
		up(p);
	}
	void merge ( int &u , int v , int l , int r )
	{
		if ( !u || !v ) return u += v , void();
		if ( l == r ) return t[u].maxx += t[v].maxx , t[u].maxdep = l , recycle(v) , void();
		merge ( ls(u) , ls(v) , l , mid ) , merge ( rs(u) , rs(v) , mid + 1 , r );
		up(u) , recycle(v);
	}
	void dfs ( int u , int f )
	{
		dep[u] = dep[f] + 1;
		rt[u] = new_node();
		for ( auto v : e[u] ) if ( v ^ f ) dfs ( v , u ) , merge ( rt[u] , rt[v] , 1 , n );
		upd ( rt[u] , 1 , n , dep[u] );
		ans[u] = t[rt[u]].maxdep - dep[u];
	}
}T;



signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read();
	for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
	T.dfs ( 1 , 0 );
	for ( int i = 1 ; i <= n ; i ++ ) cout << ans[i] << endl;
	return 0;
}

Blood Cousins Return

相当于是HH的项链+Blood cousins return的结合体

用线段树合并维护节点 为每一个叶子节点单独开一个set即可 合并时将两个set用启发式合并来合并

空间可以压到O(n) 时间复杂度O(nlog2n)

upd:空间O(n)应该是假的 应该只比正常空间小了一点 那还优化什么()

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#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 eb emplace_back
const int inf = 0x3f3f3f3f;
const int N = 5e5 + 5;
//char buf[1<<24] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , dep[N] , a[N] , rt[N] , ans[N];

struct que { int val , id; };
vector<que> q[N];

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

set<int> s[N];

struct seg 
{
	struct node { int son[2] , id; } t[N<<2];
	int tot = 0 , top = 0 , cntset = 0 , sta[N];
	inl int newnode() { return ++tot; }
	void merge ( int &u , int v , int l , int r )
	{
		if ( !u || !v ) return u += v , void();
		if ( l == r ) 
		{
			if ( !t[u].id ) return t[u].id = t[v].id , void();//必要?
			else
			{
				if ( s[t[u].id].size() < s[t[v].id].size() ) swap ( t[u].id , t[v].id );
				for ( auto val : s[t[v].id] ) s[t[u].id].insert(val);
				s[t[v].id].clear();//只有叶子节点需要清空set
				return;
			}
		}
		merge ( ls(u) , ls(v) , l , mid ) , merge ( rs(u) , rs(v) , mid + 1 , r );
	}
	void upd ( int &p , int l , int r , int x , int val )
	{
		if ( !p ) p = newnode();
		if ( l == r ) { if ( !t[p].id ) t[p].id = ++cntset; s[t[p].id].insert(val); return; }
		if ( x <= mid ) upd ( lson , x , val );
		else upd ( rson , x , val );
	}
	int query ( int p , int l , int r , int x )
	{
		if ( l == r ) return s[t[p].id].size();
		if ( x <= mid ) return query ( lson , x );
		else return query ( rson , x );
	}
	void solve ( int u , int f )
	{
		upd ( rt[u] , 1 , n , dep[u] , a[u] );
		for ( auto v : e[u] ) if ( f != v ) solve ( v , u ) , merge ( rt[u] , rt[v] , 1 , n );
		for ( auto [val,id] : q[u] ) ans[id] = query ( rt[u] , 1 , n , val );
	}
}T;

void dfs ( int u , int f , int d )
{
	dep[u] = d;
	for ( int v : e[u] ) if ( v != f ) dfs ( v , u , d + 1 );
}

int name;
map<string,int>mp;
string str;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read();
	for ( int i = 1 , fa ; i <= n ; i ++ )
	{
		cin >> str , fa = read();
		if ( !mp[str] ) a[i] = mp[str] = ++name;
		else a[i] = mp[str];
		add ( i , fa ) , add ( fa , i );
	}
	dfs ( 0 , -1 , 0 );
	m = read();
	for ( int i = 1 , v , k ; i <= m ; i ++ ) 
	{
		v = read() , k = read();
		if ( dep[v] + k <= n ) q[v].eb((que){dep[v]+k,i});
	}
	T.solve ( 0 , -1 );
	for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] << endl;
	return 0;
}

P3899 [湖南集训] 更为厉害

题意转化为 在一棵树中 有多少对三元组(a,b,c) 满足a,bc的祖先 且dis(a,b)k

考虑对于每一个固定的ab,c的放置位置

那么分两种情况:

  1. ba的祖先 那么bmin(dep[a]1,k)个放置位置 c必须放置在a的子树中 也就是sz[a]1 相乘即可

  2. ba的子孙 那么b可以放在a的下属节点中深度为[dep[a]+1,min(dep[a]+k,n)]的节点中 那么对于每一个b节点 可以放置的c点位置即为sz[b]1

    那么开一个以dep为下标的权值线段树 节点权值为sz[u]1即可统计

改回了原来的合并写法 感觉更清晰一点()

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define endl '\n'
#define inl inline
#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 eb emplace_back
#define int long long
const int N = 3e5 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
//#define getchar() cin.get();
int read()
{
	int x = 0 , f = 1;
	char ch = getchar();
	while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
	while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
	return x * f ;
}

int n , m , rt[N] , ans[N] , sz[N] , dep[N];

struct que { int val , id; };
vector<que> q[N];

vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }

struct segtree
{
	struct node { int son[2] , sum; } t[N<<5];
	int tot = 0;
	inl void up ( int p ) { t[p].sum = t[ls(p)].sum + t[rs(p)].sum; }
	void upd ( int &p , int l , int r , int x , int val )
	{
		if ( !p ) p = ++tot; t[p].sum += val;
		if ( l == r ) return;
		if ( x <= mid ) upd ( lson , x , val );
		else upd ( rson , x , val );
	}
	void merge ( int &p , int l , int r , int u , int v )
	{
		if ( !u || !v ) return p = u + v , void();
		if ( l == r ) return t[p].sum = t[u].sum + t[v].sum , void();
		merge ( lson , ls(u) , ls(v) ) , merge ( rson , rs(u) , rs(v) );
		up(p);
	}
	int query ( int p , int l , int r , int x , int y )
	{
		if ( x > y ) return 0;
		if ( x <= l && r <= y ) return t[p].sum;
		int res = 0;
		if ( x <= mid ) res += query ( lson , x , y );
		if ( mid + 1 <= y ) res += query ( rson , x , y );
		return res;
	}
	void dfs ( int u , int f )
	{
		dep[u] = dep[f] + 1 , sz[u] = 1 , rt[u] = ++tot;
		for ( auto v : e[u] )
			if ( v ^ f )
			{
				dfs ( v , u );
				sz[u] += sz[v];
				merge ( rt[u] , 1 , n , rt[u] , rt[v] );
			}
		upd ( rt[u] , 1 , n , dep[u] , sz[u] - 1 );
		for ( auto [k,id] : q[u] ) ans[id] = ( sz[u] - 1 ) * min ( dep[u] - 1 , k ) + query ( rt[u] , 1 , n , dep[u] + 1 , min ( dep[u] + k , n ) );
	}
}T;

signed main ()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr) , cout.tie(nullptr);
	n = read() , m = read();
	for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
	for ( int i = 1 , x , y ; i <= m ; i ++ )
	{
		x = read() , y = read();
		q[x].eb((que){y,i});
	}
	T.dfs ( 1 , 0 );
	for ( int i = 1 ; i <= m ; i ++ ) cout << ans[i] << endl;
	return 0;
}
posted @   Echo_Long  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示