2022CCPC女生赛-L.彩色的树(线段树合并)

 

链接Problem - L - Codeforces

以前迷迷糊糊用dsu on tree写的题目 但是其实没搞明白 现在换一种写(太菜了还是没搞明白dsu on tree)

题意:

给你一棵树,询问给定询问的节点上,子树内距离小于等于k的节点不同颜色的种类有多少个。k是固定的值。

解法:

本题做法为比较板子的线段树合并,如果您没有接触过,可以先写几道简单的学习一下。

 

对线段树维护颜色的数量,对于一个节点,它要给自己代表的颜色+1,要给它往上数k+1个点的位置把自己的颜色-1

然后线段树维护每个颜色出现的数量以及出现颜色的种类,这个很好写,要合并所以需要动态开点。

 

一个点往上数k个点就是用树上倍增往上面跑 是log的

 

问题需要离线。

 

在dfs的过程中,需要把子树的树合并到父亲上,然后处理点的+1-1,然后处理询问。

由于是线段树合并,复杂度取决于小的那个树的大小,因此整体的时间和空间复杂度都是nlogn

附上ac代码

seg表示某个颜色的出现次数

sum表示所有颜色中 出现了的颜色的总数

u点表示以u为根节点,子树中所有距离小于等于k的节点出现的颜色信息的线段树的根节点(所以区间查就查sum[u] 就行了)

复制代码
#include<bits/stdc++.h>
#define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
typedef long long ll;
const ll MAXN =  1e5+7;
const ll mod = 1e9+7;
const ll inf = 0x3f3f3f3f;
const ll R = 40;
int dep[MAXN], fa[MAXN][22];int n,k;
int c[MAXN];
vector<int> adj[MAXN];
int cnt=0;
vector<int> Q[MAXN];
vector<int> op[MAXN];
int seg[MAXN*R],ls[MAXN*R],rs[MAXN*R],sum[MAXN*R];
int ans[MAXN];
void dfs(int u, int par, int w) {
    dep[u] = dep[par] + 1;
    fa[u][0] = par;
    for (int i = 1; i <= 20; ++i) {
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    for (auto &v: adj[u]) {
        if (v == par) continue;
        dfs(v, u, w);
    }
}
int find(int x){
    int tmp=k+1;
    for(int i=20;i>=0;i--){
        if((1LL<<i)<=tmp){
            tmp-=(1LL<<i);
            x=fa[x][i];
        }
    }
    return x;
}

void up(int id){
    sum[id]=sum[ls[id]]+sum[rs[id]];
}
int merge(int a,int b,int l,int r){
    if(!a) return b;
    if(!b) return a;
    if(l==r){
        seg[a]+=seg[b];
        sum[a]=max(sum[a],sum[b]);
        return a;
    }
    int mid=l+r>>1;
    ls[a]=merge(ls[a],ls[b],l,mid);
    rs[a]=merge(rs[a],rs[b],mid+1,r);
    up(a);
    return a;
} 
void insert(int &id,int l,int r,int pos,int val){
    if(!id) id=++cnt;
    if(l==r){
        if(val==1&&seg[id]==0){
            sum[id]=1;
        }
        else if(val==-1&&seg[id]==1){
            sum[id]=0;
        }
        seg[id]+=val;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid) insert(ls[id],l,mid,pos,val);
    else insert(rs[id],mid+1,r,pos,val);
    up(id);
}
void dfs(int u,int fa){
    for(auto &v:adj[u]){
        if(v==fa) continue;
        dfs(v,u);
        merge(u,v,1,n);
    }
    insert(u,1,n,c[u],1);
    for(auto &it:op[u]) insert(u,1,n,it,-1);
    for(auto &it:Q[u]){
        ans[it]=sum[u];
    }
}
void solve(){
    cin>>n>>k;
    cnt=n;
    for(int i=1;i<=n;i++){
        cin>>c[i];
    }
    for(int i=1;i<n;i++){
        int u,v;cin>>u>>v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    dfs(1,0,0);
    for(int i=1;i<=n;i++){
        if(dep[i]<=k+1) continue;
        int x=find(i);
        op[x].push_back(c[i]);
    }
    int q;cin>>q;
    for(int i=1;i<=q;i++){
        int x;cin>>x;
        Q[x].push_back(i);
    }
    dfs(1,0);
    for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
}
signed main(){
    close;
    solve();
}
/*
7 2
1 1 2 1 1 1 2
1 2
1 3
2 4
2 5
5 7
3 6
7
1 2 3 4 5 6 7

*/
View Code
复制代码

 

posted @   xishuiw  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示