题目链接:点击查看

题目大意:给出一棵无向树,每个节点都有一种颜色,接下来时m次操作:

  1. 1 x y:将x及其子树染成y的颜色
  2. 2 x:查询x及其子树上共有多少种不同的颜色

题目分析:看完这个题的第一反应就是直接暴力跑跑试试,因为树的话时间复杂度是logn,暴力的话也才nlogn,可是等WA了一发后我才意识到,logn级别的是二叉树才能实现的时间精度,如果最差的情况,树是一条直线分布,那时间复杂度到了n*n,在这个题目中必然超时,但给我的结果时WA我还是有点意外的。。所以我们需要考虑另外的方法,因为这个题目是放在了线段树的专题中,而且时关于子树的操作,我们可以先将每个点用dfs序排个序,将每个顶点的子树放到一起,将树线性化,这样就能将对子树的一系列操作转换为对区间的一系列操作,进而可以在线段树上进行操作,达到真正查找和修改都是logn的时间复杂度了。关于对颜色种类的处理可以使用状态压缩,之前做过一个很类似的题目。

补充一下,有个细节,之前都没注意到过的,就是对状态进行压缩时,要用longlong这个没问题,就是(1<<i)这个语句,一定一定要记得在1后面加上一个LL,写成这样才行:(1LL<<i),因为这个细节,WA了一晚上

直接上代码吧,更多的会在注释中解释:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<set>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=4e5+100;int a[N];int cnt;LL color[N];int point[N];int num[N];//dfs的时候顺便记录一下子树上的节点数量,可以依次来确定子树所在的区间vector<int>node[N];void dfs(int u,int fa)//dfs序给每个节点编号
{num[u]=1;color[cnt]=(1LL<<a[u]);point[u]=cnt++;for(int i=0;i<node[u].size();i++){int v=node[u][i];if(v==fa)continue;if(num[v])continue;dfs(v,u);num[u]+=num[v];}
}struct Node//线段树维护区间颜色状态,和之前做过的一个题很类似
{int l,r;LL color;bool lazy;
}tree[N<<2];void pushup(int k)
{tree[k].color=(tree[k<<1].color|tree[k<<1|1].color);
}void pushdown(int k)
{tree[k<<1].lazy=tree[k<<1|1].lazy=true;tree[k<<1].color=tree[k<<1|1].color=tree[k].color;tree[k].lazy=false;
}void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].lazy=false;if(l==r){tree[k].color=color[l];return;}int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}void update(int k,int l,int r,int val)
{if(tree[k].l>r||tree[k].r<l)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].color=(1LL<<val);tree[k].lazy=true;return;}if(tree[k].lazy)pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k);
}LL query(int k,int l,int r)
{if(tree[k].l>r||tree[k].r<l)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].color;if(tree[k].lazy)//这里不要忘记下传懒标记pushdown(k);return query(k<<1,l,r)|query(k<<1|1,l,r);
}int main()
{
//  freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){for(int i=1;i<=n;i++){scanf("%d",a+i);node[i].clear();}for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);node[u].push_back(v);node[v].push_back(u);}memset(num,0,sizeof(num));memset(color,0,sizeof(color));memset(point,0,sizeof(point));cnt=1;dfs(1,-1);build(1,1,n);while(m--){int op;scanf("%d",&op);if(op==1){int x,y;scanf("%d%d",&x,&y);update(1,point[x],point[x]+num[x]-1,y);}else{int x;scanf("%d",&x);LL ans=query(1,point[x],point[x]+num[x]-1);int tt=0;while(ans){if(ans&1)tt++;ans>>=1;}printf("%d\n",tt);}}}return 0;
}

后来做题又学会了一种我感觉比较好的dfs序的方法,就是用L和R两个数组来维护某个点的子树区间:

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<set>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=4e5+100;int a[N];int cnt;LL color[N];int L[N],R[N];vector<int>node[N];void dfs(int u,int fa)
{L[u]=++cnt;color[cnt]=(1LL<<a[u]);for(int i=0;i<node[u].size();i++){int v=node[u][i];if(v==fa)continue;dfs(v,u);}R[u]=cnt;
}struct Node
{int l,r;LL color;bool lazy;
}tree[N<<2];void pushup(int k)
{tree[k].color=(tree[k<<1].color|tree[k<<1|1].color);
}void pushdown(int k)
{tree[k<<1].lazy=tree[k<<1|1].lazy=true;tree[k<<1].color=tree[k<<1|1].color=tree[k].color;tree[k].lazy=false;
}void build(int k,int l,int r)
{tree[k].l=l;tree[k].r=r;tree[k].lazy=false;if(l==r){tree[k].color=color[l];return;}int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}void update(int k,int l,int r,int val)
{if(tree[k].l>r||tree[k].r<l)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].color=(1LL<<val);tree[k].lazy=true;return;}if(tree[k].lazy)pushdown(k);update(k<<1,l,r,val);update(k<<1|1,l,r,val);pushup(k);
}LL query(int k,int l,int r)
{if(tree[k].l>r||tree[k].r<l)return 0;if(tree[k].l>=l&&tree[k].r<=r)return tree[k].color;if(tree[k].lazy)pushdown(k);return query(k<<1,l,r)|query(k<<1|1,l,r);
}int main()
{
//  freopen("input.txt","r",stdin);int n,m;while(scanf("%d%d",&n,&m)!=EOF){for(int i=1;i<=n;i++){scanf("%d",a+i);node[i].clear();}for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);node[u].push_back(v);node[v].push_back(u);}memset(color,0,sizeof(color));cnt=0;dfs(1,-1);build(1,1,n);while(m--){int op;scanf("%d",&op);if(op==1){int x,y;scanf("%d%d",&x,&y);update(1,L[x],R[x],y);}else{int x;scanf("%d",&x);LL ans=query(1,L[x],R[x]);int tt=0;while(ans){if(ans&1)tt++;ans>>=1;}printf("%d\n",tt);}}}return 0;
}

CodeForces - 620E New Year Tree(线段树+dfs序+状态压缩)相关推荐

  1. bzoj3252攻略(线段树+dfs序)或者(树链剖分+dfs)

    3252: 攻略 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 1341 Solved: 642 [Submit][Status][Discuss] ...

  2. BZOJ_3252_攻略_线段树+dfs序

    BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...

  3. Codeforces 877 E Danil and a Part-time Job(线段树+dfs序)

    题目地址 题意:给你一棵树,1为根节点,每个节点都有应该状态0或者1,你有两种操作,pow的操作是把该节点以及他的所有子树的每个节点进行自身异或的操作,get就是查询该节点以及他的所有子树的每个节点有 ...

  4. HDU5692(线段树+dfs序)

    Snacks Time Limit:5000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  5. Change FZU - 2277(线段树+dfs序)

    There is a rooted tree with n nodes, number from 1-n. Root's number is 1.Each node has a value ai. I ...

  6. bzoj3252攻略(线段树+dfs序)

    3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 562  Solved: 238 [Submit][Status][Discuss] ...

  7. 苹果树(线段树+Dfs序)

    1228 苹果树  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond 题目描述 Description 在卡卡的房子外面,有一棵苹果树.每年的春天,树上总会结 ...

  8. HDU - 3974 Assign the task (线段树 + dfs序)

    HDU - 3974 题意:有个公司有一些关系,每个人(除了boss)都有且仅有一个上司,这就是一棵树的关系,然后会有一些操作,C i,询问第i个人现在的任务,T x y,把y任务给x, 给x相当于给 ...

  9. POJ 2777 Count Color (线段树区间修改 + 状态压缩)

    题目链接:POJ 2777 Count Color [题目大意] 给你 n 块板子, 编号1--n , 板子的颜色最多30种, 初始时  板子的颜色都是 1: 有两种操作 1 .把给定区间的板子染成一 ...

最新文章

  1. 计算机基础资料管理办法,计算机基础知识试题(答案_)资料.doc
  2. hive like 模糊匹配
  3. 目标检测技术演化:从R-CNN到Faster R-CNN
  4. 面试官扎心一问:Tomcat 在 SpringBoot 中是如何启动的?
  5. window开机 关机 记录日志
  6. 滴滴悬赏百万寻凶,机智网友支付宝钓鱼转账杀害空姐明珠疑凶
  7. 为老电脑装linux系统
  8. 局域网入侵检测过程详解
  9. Pytho 常见模块 / 用法备忘录
  10. c4d在运行时候显示计算机内存不足,C4D R19 保存空文档时显示没有足够内存怎么办 空文档都不行 存哪都不行...
  11. yolov5的anchors及bbox的编解码原理
  12. 香港公司银行开户需要注意。
  13. 产业高地、价值洼地,雨花一流营商环境是如何炼成的?
  14. 逍遥情缘服务器维护没通告,【维护公告】2月2日中午12:00维护公告
  15. Docker部署Flask网站
  16. java共享经济项目分享_共享创业项目平台哪个好(分享5个共享创业项目)
  17. FinalData 数据恢复
  18. Linux网络bug,Linux系统下的网络带宽测速
  19. iOS VS Android ,10年之战,谁是最后赢家?
  20. UBOOT学习1——UBOOT移植流程

热门文章

  1. mybatis-批量更新
  2. MyBatis 实际使用案例-transactionManager
  3. 扩展--使用队列来优化递归操作完成文件下载
  4. 拼接字符串的两种方式
  5. MySQL常用存储引擎之Archive
  6. 手写自定义注解实现思路
  7. 我什么计算机作文600字,我家的电脑作文600字
  8. 笔试编程常用函数(Java)
  9. Java语言中的注释有哪些
  10. J-LINK不能烧写(错误:JLink Warning: RESET (pin 15) high, but should be low. Please check target)