首页 > 其他分享 >2022CCPC女生赛-L.彩色的树(线段树合并)

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

时间:2024-02-03 15:11:55浏览次数:20  
标签:ll 女生 2022CCPC int 线段 MAXN 颜色 id

 

链接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

 

标签:ll,女生,2022CCPC,int,线段,MAXN,颜色,id
From: https://www.cnblogs.com/xishuiw/p/18004799

相关文章

  • 【模板】 与等差数列结合的线段树
    题面代码点击查看代码#include<bits/stdc++.h>usingnamespacestd;#defineIOSios::sync_with_stdio(0);cin.tie(0);cout.tie(0);#definerep(i,a,n)for(inti=a;i<=n;i++)#defineper(i,a,n)for(inti=n;i>=a;i--)#definefifirst#definesesecond#defin......
  • [算法学习笔记01]线段树
    #[算法学习笔记01]线段树###每日蒟蒻小故事(1/1)蒟蒻刚刚升到S组,发现S组正在学习线段树Ⅲ.蒟蒻并不知道什么是线段树.蒟蒻十分害怕,向大佬询问什么是线段树.大佬邪魅一笑,并未解释.于是可怜的蒟蒻什么也听不懂,只得在洛谷和OIWIKI上自学线段树.“所以什么是线段树?”###什么......
  • 线段树合集
    线段树合集线段树:https://oi-wiki.org/ds/seg/扫描线:https://oi-wiki.org/geometry/scanning/orhttps://zhuanlan.zhihu.com/p/103616664orhttps://www.cnblogs.com/Rakan-LoveJ/p/11401851.html标记永久化:https://www.cnblogs.com/wozaixuexi/p/9462461.html李超线段树:ht......
  • 【学习笔记】 - 可持久化数据结构初步:可持久化线段树
    前置知识:权值线段树权值线段树每个节点不再是区间,而是值在某个范围内的个数可以用于求区间第\(k\)大值动态开点一个点只有在需要时才被创建正文什么是可持久化数据结构就是说这个数据结构能保留每一个历史版本且支持操作可持久化线段树又称函数式线段树/主席树......
  • 动态区间特殊值——线段树的简单应用
    目录问题引入简单介绍功能详情碎碎念问题引入很多地方写的是区间最值问题,感觉区间特殊值更好一些,区间gcd、前缀和都可以,问题描述大致就是给出一个数组,要求给出询问区间的特殊值,当然求和之类的可以用树状数组解决,但是最大、最小等等特殊值是不能转化为前缀和用树状数组解决的,因此......
  • 【学习笔记】线段树
    1.线段树合并P4556雨天的尾巴首先我们可以把链上操作转成树上差分。然后我们对每个节点开一棵权值线段树。朴素的树上差分都是开一个桶然后加和,但是这里开的是线段树。所以就有了线段树合并。在把\(y\)并到\(x\)的过程中,如果\(x\)本身没有一个\(y\)有的节点就可以直......
  • 线段树笔记
    voidpushup(inttr){ seg[tr]=seg[tr*2]+seg[tr*2+1];}voidbuild(inttr,intl,intr){ if(l==r){ seg[tr]=a[r]; return; } intmid=(l+r)/2; build(tr/2,l,mid); build(tr/2,mid+1,r); pushup(tr);}voidpushdown(inttr,intl,intr){ if(pls[tr]==0)......
  • 龙哥量化:缠论的笔、线段、中枢以及MACD背离分析实现
    声明:看到研究非常细致深入的文章,转载到我的博客园,以便学习和研究。(转载自聚宽的大象咖啡)本文参考了如下相关文贴:【量化缠论】之分型、笔、线段识别1.1.在该帖的基础上将线段和调整后的k线绘制在一张图中;1.2.添加了中枢判断,并和调整后的k线绘制在一张图中;《MACD背离》技术......
  • 【模板】可持久化线段树 2
    就是区间第k大,原题链接这个是用整体二分做的这个是用可持久化线段树做的线段树还是快一点啊#include<bits/stdc++.h>#definelllonglongusingnamespacestd;inlinellread(){ charc=getchar();lla=0,b=1; for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1; ......
  • Legacy (线段树优化建图)
    题目链接:Legacy-洛谷|计算机科学教育新生态(luogu.com.cn)题解:考虑题目中一个点向区间连边,如真的对区间中的每一点分别连边后跑最短路,时间空间都要炸。因为是一个点向区间连边,考虑线段树。1到n构造两颗区间线段数 观察上图(从网上搬的)两颗线段树,一颗入树父亲向儿子连......