线段树合并
线段树合并
实际上全称为"动态开点权值线段树合并"
可以用来维护两棵动态开点权值线段树的合并问题
P4556 [Vani有约会] 雨天的尾巴 /【模板】线段树合并
板子题 用到了一点差分的思想
我们对于每一个点开一棵以种类为下标的权值线段树 上面维护的是区间内种类最多的值
那么对于树上点的差分 若想修改
最后做一遍树上前缀和统计即可 对于子树的合并直接线段树合并即可
#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] 永无乡
基本未看题解写出来的题 过编即
区间
用并查集维护连通块 动态开点线段树维护连通块内权值
查询时直接线段树上二分即可
因为要求岛的编号 那么我们还需要维护一个重要度对应的
注意初始化时每一个连通块的线段树都要初始化 并且
#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
对于任意一个节点 交换左右子树对当前节点和前面的节点都没有影响
依照经典树上统计问题 对于一个节点 有三种逆序对
- 在左子树中
- 在右子树中
- 以当前节点作为
的逆序对
因为我们已经搞完左子树的贡献和右子树的贡献 所以我们只需要考虑第三种情况即可
显而易见地 交换子树只会对第三种情况有影响 因此我们只需要在合并线段树的同时 统计交换子树的逆序对个数
需要注意的是 线段树合并时
个人理解是 如果
#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
线段树合并写法 还有
将询问离线 对每次询问 在
当
最后 因为输出的是表亲的个数 那么我们要将
#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
一眼线段树合并 节点以
采用了空间回收的写法 需要边插入点边合并 而不能将所有点都插入之后再合并 这就需要提前给
(实际上不采用空间回收也可以过 朴素线段树合并+按照题解的空间开即可)
改了一下线段树合并的马蜂 感觉更清晰一点 注意线段树合并的时候 如果
#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
相当于是
用线段树合并维护节点 为每一个叶子节点单独开一个
空间可以压到
#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 [湖南集训] 更为厉害
题意转化为 在一棵树中 有多少对三元组
考虑对于每一个固定的
那么分两种情况:
-
是 的祖先 那么 有 个放置位置 必须放置在 的子树中 也就是 相乘即可 -
是 的子孙 那么 可以放在 的下属节点中深度为 的节点中 那么对于每一个 节点 可以放置的 点位置即为那么开一个以
为下标的权值线段树 节点权值为 即可统计
改回了原来的合并写法 感觉更清晰一点()
#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;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战