题目链接:点击查看

题目大意:给出一棵 n 个节点构成的数,每个节点都有一个颜色,现在需要执行 m 次操作,每次操作分为如下两种类型:

  1. Q x:回答所有颜色为 x 的节点构成的连通子图含有的最少边数
  2. U x y:将点 x 的颜色修改为 y

题目分析:首先转换模型,要求边的个数,不妨转换成距离

根据虚树建树得到启发,如果目前只有两个节点的话,那么该题对应的答案显然是两点之间的距离,对应为下图:

如果此时再加入一个点 2,满足 dfn[ 1 ] < dfn[ 2 ] < dfn[ 3 ],那么对于点 2 来说无非只有两种情况:

  1. 不位于点 1 到点 3 的路径上
  2. 位于点 1 到点 3 的路径上

分别对应着下图的两种情况:

对于情况二来说,在本题中对答案显然不做贡献,而对于情况一来说,多出来的一段贡献已经用红圈标记出来了

总结一下求这个红圈中的贡献,实质上就是求 dis( 1 , 2 ) + dis( 2 , 3 ) - dis( 1 , 3 )

其中,dis( x , y ) 是从点 x 到点 y 的距离,这个借助 LCA 很容易就能实现,这样本题的做法就呼之欲出了

求出整棵树的 dfs 序,将每个颜色的节点按照 dfs 序维护,对于每个节点 x 来说,设 l 为 dfs 序小于 x 的最大节点,r 为 dfs 序大于 x 的最小节点,mmin 为颜色 c[ x ] 中 dfs 序最小的节点,mmax 为颜色 c[ x ] 中 dfs 序最大的节点,加入点 x 分为两种情况讨论:

  1. 如果 l 和 r 都存在:ans += dis( x , l ) + dis( x , r ) - dis( l , r ),对应着上图中的情况
  2. 否则:ans += dis( x , mmin ) + dis( x , mmax ) - dis( mmin , mmax ),对应着新加入的点的 dfn 为最小值或最大值的情况,此时该点对本题的答案一定具有贡献,随便找两个点配合更新一下答案即可

删除的话倒着减去贡献就好了

代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=1e5+100;int limit,deep[N],dp[N][20],id[N],c[N],ans[N],cnt;vector<int>node[N];struct Scmp
{bool operator()(int x,int y){return id[x]<id[y];}
};set<int,Scmp>st[N];void dfs(int u,int fa,int dep)
{id[u]=++cnt;deep[u]=dep;dp[u][0]=fa;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(auto v:node[u]){if(v==fa)continue;dfs(v,u,dep+1);}
}int LCA(int x,int y)
{if(deep[x]<deep[y])swap(x,y);for(int i=limit;i>=0;i--)if(deep[x]-deep[y]>=(1<<i))x=dp[x][i];if(x==y)return x;for(int i=limit;i>=0;i--)if(dp[x][i]!=dp[y][i]){x=dp[x][i];y=dp[y][i];}return dp[x][0];
}int dis(int x,int y)
{return deep[x]+deep[y]-2*deep[LCA(x,y)];
}void update(int x,int flag)
{if(flag==-1)st[c[x]].erase(x);auto it=st[c[x]].lower_bound(x);if(st[c[x]].empty())ans[c[x]]=0;else if(it==st[c[x]].begin()||it==st[c[x]].end()){ans[c[x]]+=flag*dis(*st[c[x]].begin(),x);ans[c[x]]+=flag*dis(*st[c[x]].rbegin(),x);ans[c[x]]-=flag*dis(*st[c[x]].begin(),*st[c[x]].rbegin());}else{auto nt=it,pre=--it;ans[c[x]]+=flag*dis(*nt,x);ans[c[x]]+=flag*dis(*pre,x);ans[c[x]]-=flag*dis(*pre,*nt);}if(flag==1)st[c[x]].insert(x);
}int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);int n;scanf("%d",&n);limit=log2(n)+1;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);}dfs(1,0,0);for(int i=1;i<=n;i++){scanf("%d",c+i);update(i,1);}int m;scanf("%d",&m);while(m--){char op[5];scanf("%s",op);if(op[0]=='Q'){int x;scanf("%d",&x);printf("%d\n",st[x].empty()?-1:ans[x]/2);}else{int x,y;scanf("%d%d",&x,&y);update(x,-1);c[x]=y;update(x,1);}}return 0;
}

牛客 - Colorful Tree(dfs序+LCA)相关推荐

  1. BZOJ2588 Count on a tree DFS序+LCA+值域主席树

    Count on a tree 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答 ...

  2. 牛客多校2 - Cover the Tree(dfs序)

    题目链接:点击查看 题目大意:给出一棵无根树,问能否选择数量最少的链,使得所有的路径都被覆盖到 题目分析:读完题后不难看出,假设叶子结点的个数为 x,那么答案就是 x / 2 向上取整 然后说结论,d ...

  3. CodeForces - 1328E Tree Queries(dfs序/LCA)

    题目链接:点击查看 题目大意:给出一棵以点 1 为根节点的树,接下来有 m 次询问,每次询问给出 k 个点,题目问我们能否找到一个点 u ,使得从根节点到点 u 的简单路径,到 k 个点的每个点的距离 ...

  4. [codeforces] 383C Propagating tree(dfs序+线段树)

    题意: 给你一棵n个结点的树, 以1为根.每个结点有点权.有m次操作: 1.x结点权值 +val,x的儿子权值 −val,x的孙子们 +val,以此类推. 2.询问x的点权: 题解: 我们首先跑一边d ...

  5. Innumerable Ancestors 尺取 dfs序 lca

    给一棵树,m次查询,每次查询给两个集合,从这两个集合中分别选一个结点,使得这两个结点的lca的深度最大 考虑dfs序为3, 4, 5的三个结点,3和4的lca深度一定大于等于3和5的lca深度 所以可 ...

  6. [Split The Tree][dfs序+树状数组求区间数的种数]

    Split The Tree 时间限制: 1 Sec  内存限制: 128 MB 提交: 46  解决: 11 [提交] [状态] [讨论版] [命题人:admin] 题目描述 You are giv ...

  7. 牛客网——字符串逆序

    牛客-字符串倒置 题目链接 解题思路 指针解法 题目链接 链接: link 这个是倒置字符串题目的链接,有兴趣做的小伙伴可以点击前往 解题思路 思维方式: 下面我们将逐步进行对问题的分析: 1.我们可 ...

  8. Apple Tree(dfs序+树状数组)

    题目(传送门poj3321) Apple Tree Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 40714 Accepted: ...

  9. poj 3321 Apple Tree(dfs序+树状数组求和模型)

    题目链接:http://poj.org/problem?id=3321 解题思路: 先dfs求出序列,将子树转化到dfs序列的区间内,接下来就是简单的树状数组求和模型了.水题. #include< ...

最新文章

  1. 学生生涯---真的就这样逝去了!后悔已不足以!
  2. zipaligin不是内部或外部命令,也不是可运行的程序
  3. Docker volume使用
  4. 【网络爬虫入门04】彻底掌握BeautifulSoup的CSS选择器
  5. 基于协同过滤,NMF和Baseline的推荐算法
  6. 只能选择GridView中的一个CheckBox(单选CheckBox)
  7. 大数据Hadoop快速入门
  8. miou 代码 VOC2012
  9. 修改华为 Echolife HG8010h 的超级用户密码
  10. 用javascript实现节假日自动切换风格
  11. dell笔记本指示灯闪烁_解决笔记本电源灯一闪一闪问题【图文】
  12. STM32F103的SPI口进行OLED屏的使用
  13. 决战平安京选择正确的服务器,决战!平安京怎么预先选择自己的位置 匹配预选位流程攻略...
  14. Microsoft Visual Studio 2013 产品密匙
  15. python学习之路08(正则表达式和网络)
  16. 打破行业困境,大麦如何引领NB-IoT技术的创新应用
  17. 4、Screenshot-adb基本命令-Android
  18. 程序人生路上的新阶段
  19. android 家庭理财软件 需求分析,基于Android的家庭理财通的设计与实现.docx
  20. 计算机制造技术飞速发展 使用计算机,机械设计制造及其自动化中计算机技术的应用分析...

热门文章

  1. 两线怎么接三线插座图_水温传感器怎么判断好坏
  2. RabbitMQ工作线程代码
  3. 基于Xml 的IOC 容器-将配置载入内存
  4. 用户退出登录清空cookie
  5. SpringMVC异常处理机制-异常处理的思路
  6. springboot 切换日志实现
  7. 分布式文件系统研究-搭建图片服务虚拟主机
  8. OutputStreamWriter介绍代码实现
  9. tomcat_动态java项目的目录结构
  10. self-在类的外部给对象增加属性