题目链接:点击查看

题目大意:给出一棵由n+1个节点组成的数,节点编号为0~n(这个无关紧要,预处理时所有节点以及n都自增1即可转换为正常的模型了),现在给出m组节点u和v,问若想让m组u和v都断开连接,则最少需要去掉几个点

题目分析:首先我们知道,若想让u和v断开连接,最优解肯定是切断其lca,毕竟是最近公共祖先嘛,当然切断u和切断v也可以满足断开连接,但并不是最优解,因为切断lca之后,lca下面的所有不同链上的节点都不可能互相到达了,相当于将包含lca在内的子树从主树上拿了下来,这样可以达到切断一个点,既能满足u和v断开连接,也能让最多的点互不相连

不过这就面临着一个问题,若切断了当前的lca,下一组u和v的lca比当前lca的深度要深,那该怎么判断呢,也就是说下一组的u和v虽然位于当前lca的子树中,但却并不受其影响,因为他们的lca比当前的lca深度还要深

这时候我们就可以根据每组u和v的lca的深度进行排序了,优先处理深度大的lca肯定是最优解,因为深度越大的lca所包含的子树就越小,当处理完深度较大的lca后,用线段树将其子树标记一下,在进行后续操作的时候,若u或v中至少有一点被标记过了,说明了被标记的点所在的子树已经与主树脱离了,也就是说不需要断掉任何点就已经无法相互连接了(因为前面断掉的lca充当了当前u与v的连接点),若u与v都没有被标记,那么直接断掉其lca即可,也是有一点贪心的策略

感叹一下吧,这是上树一来第一道从有思路开始到实现为止一路通顺的题目,一口气写完代码,一遍过样例,最后1A,还是有点小开心的

这个题的线段树我看网上很多大佬用树状数组代替的,我感觉区别不大,主要是我不会树状数组。。所以用线段树做也是没什么问题的

代码:

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
#include<cctype>
#include<stack>
#include<queue>
#include<list>
#include<vector>
#include<set>
#include<map>
#include<sstream>
using namespace std;typedef long long LL;const int inf=0x3f3f3f3f;const int N=1e4+100;int n,limit;vector<int>node[N];int deep[N],dp[N][20],L[N],R[N],cnt;//树上倍增用void dfs(int u,int f,int dep)
{L[u]=++cnt;//记录一下dfs序deep[u]=dep;dp[u][0]=f;for(int i=1;i<=limit;i++)dp[u][i]=dp[dp[u][i-1]][i-1];for(auto v:node[u]){if(v==f)continue;dfs(v,u,dep+1);}R[u]=cnt;
}int LCA(int a,int b)//求LCA
{if(deep[a]<deep[b])swap(a,b);for(int i=limit;i>=0;i--)if(deep[a]-deep[b]>=(1<<i))a=dp[a][i];if(a==b)return a;for(int i=limit;i>=0;i--)if(dp[a][i]!=dp[b][i]){a=dp[a][i];b=dp[b][i];}return dp[a][0];
}struct Node
{int u,v,lca;bool operator<(const Node& a)const{return deep[lca]>deep[a.lca];}
}a[N*5];struct tree
{int l,r;bool lazy;
}tree[N<<2];//线段树维护一个lazy标记即可void pushdown(int k)
{tree[k<<1].lazy=tree[k<<1|1].lazy=true;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)return;int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}void update(int k,int l,int r)
{if(tree[k].r<l||tree[k].l>r)return;if(tree[k].l>=l&&tree[k].r<=r){tree[k].lazy=true;return;}if(tree[k].lazy)pushdown(k);update(k<<1,l,r);update(k<<1|1,l,r);
}bool query(int k,int pos)
{if(tree[k].l==tree[k].r)return tree[k].lazy;if(tree[k].lazy)pushdown(k);int mid=tree[k].l+tree[k].r>>1;if(mid>=pos)return query(k<<1,pos);elsereturn query(k<<1|1,pos);
}void init()
{for(int i=1;i<=n;i++)node[i].clear();limit=log2(n)+1;cnt=0;
}int main()
{
//  freopen("input.txt","r",stdin);
//  ios::sync_with_stdio(false);while(scanf("%d",&n)!=EOF){n++;init();for(int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);u++,v++;node[u].push_back(v);node[v].push_back(u);}dfs(1,0,0);build(1,1,n);int m;scanf("%d",&m);for(int i=1;i<=m;i++){scanf("%d%d",&a[i].u,&a[i].v);a[i].u++,a[i].v++;a[i].lca=LCA(a[i].u,a[i].v);}sort(a+1,a+1+m);int ans=0;for(int i=1;i<=m;i++){if(query(1,L[a[i].u])||query(1,L[a[i].v]))//如果u和v至少有一个被标记过了,说明已经互不相连了continue;else//若都没被标记过,断掉其lca{ans++;update(1,L[a[i].lca],R[a[i].lca]);}}printf("%d\n",ans);}return 0;
}

HDU - 6203 ping ping ping(LCA+dfs序+线段树)相关推荐

  1. codeforces E. Jamie and Tree LCA+dfs序+线段树

    题解: 写起来还稍微有点麻烦. dfs序+线段树可以维护子树的整体修改和查询. 因此,这道题我们要往子树上靠. 我们首先从1号点进行dfs遍历,顺便求出点的dfs序和深度,然后我们采用倍增的思想,可以 ...

  2. POJ - 2763 Housewife Wind LCA+dfs序+线段树

    q次询问求两个点之间的距离,并且可以随时修改某条边的长度,最短距离可以用lca来求,但是树上维护每一个点到root的距离必须要用dfs序来记录时间戳,在dfs的时候顺便记录每一条边(u,v)对应的v节 ...

  3. Jittery Roads Gym - 100889J (虚树 + DP + dfs 序, + 线段树)

    每次给一个点集, 求每个点到其他所有点的最大距离: 会修改边权; 修改边权之后, 我们可以用 dfs 序 + 线段树维护 当前点到根节点的距离. 还可以用树状数组 + 差分思想 维护. { dfs 序 ...

  4. New Year Tree(dfs序+线段树+二进制)

    题意: 给出一棵 n个节点的树,根节点为 1.每个节点上有一种颜色 ci.m次操作.操作有两种: 1 u c:将以 u为根的子树上的所有节点的颜色改为c. 2 u:询问以 u为根的子树上的所有节点的颜 ...

  5. 求和(dfs序+线段树)

    题意: 已知有n个节点,有n−1条边,形成一个树的结构. 给定一个根节点k,每个节点都有一个权值,节点i的权值为vi​. 给m个操作,操作有两种类型: 1 a x :表示将节点a的权值加上x 2 a ...

  6. 【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

    题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\), ...

  7. Codeforces 343D Water Tree(DFS序 + 线段树)

    题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...

  8. [CodeForces877 E. Danil and a Part-time Job]dfs序+线段树

    [CodeForces877 E.Danil and a Part-time Job]dfs序+线段树 分类:Data Structure SegMent Tree dfn 1. 题目链接 [Code ...

  9. bzoj3252攻略 贪心+dfs序+线段树

    题目链接:戳这里 3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 605  Solved: 255 [Submit][Status] ...

最新文章

  1. 【C++】多线程与异步编程【四】
  2. 非监督HMP算法的物体识别
  3. TQ210——时钟系统
  4. 聊聊天,如果能重来,还干不干程序员?
  5. linux编译cpp文件命令,Jsoncpp Linux 下编译为 .a 文件
  6. oracle10g遇到ORA-16038日志无法归档问题
  7. php存sqlite图片,SQLite数据库如何存储图片/语音
  8. 【肌电信号】基于matlab GUI肌电信号处理【含Matlab源码 966期】
  9. 如何用C语言在控制台输出437代码页编码下的ASCII字符
  10. chm转换成txt的url顺序问题
  11. Dubbo太难了,我决定加入Spring Cloud阵营了...
  12. MapReduce 内部实现机制,你真的懂吗?
  13. 帮谷歌推广Webp图片格式之:Webp的格式转换
  14. 简单的邮箱格式校验方式
  15. c++实现currency类(两种)
  16. VC++的GDI+中,DrawImage方法的应用入门
  17. 创建一个包含1-10的立方的列表
  18. input限制只能输入数字/字母/英文符号
  19. 5G音视频时代还不学NDK开发吗?赶快收藏备战金九银十!
  20. MySQL性能优化(二):优化数据库的设计

热门文章

  1. rpm部署mysql_使用rpm快速安装部署MySQL5.6以及主从设置
  2. MySQL等值连接的示例
  3. 生成服务器证书的申请文件和私钥文件
  4. 文件下载的文件名中文乱码
  5. flume高可用-failover-配置文件编写
  6. SpringBoot_入门-HelloWorld细节-场景启动器(starter)
  7. springMVC请求流程详解
  8. uploadify php处理程序,uploadify 后台处理
  9. oracle or使用速度快马_使用mysqld_exporter监控MySQL并展示数据
  10. 基于matlab 读取文件夹 保存文件夹