题目链接:点击查看

题目大意:给出一棵 n 个节点的树,以点 1 为根,现在对于每个节点作为根的子树求解:子树中有多少个编号不相交的连续子段,如:1 2 4 5 7,共有三个连续的段,分别为 [ 1 , 2 ] , [ 4 , 5 ] , [ 7 ]

题目分析:树上启发式合并的模板题,cal 函数中直接维护一个数组用来统计加入或删除掉一个数字后对于贡献的影响即可:

  1. 如果编号 x - 1 和 x + 1 早已存在,那么加入 x 后总段数减一
  2. 如果编号 x - 1 或 x + 1 早已存在,那么加入 x 后总段数不变
  3. 如果编号 x - 1 和 x + 1 都不存在,那么加入 x 后总段数加一

删除的话正好反过来

然后,今下午在和 zx 学长闲聊的时候,意外发现这个题目可以用主席树乱搞,因为子树对应的刚好是 dfs 序,区间内有多少个不相交的连续子段也可以用线段树的区间合并来解决,兴致勃勃来到电脑前面实现,却发现了些许问题:

  1. 对于每个节点如果只维护 sum(有多少个子段),ll(最左端是否有数字),rr(最右端是否有数字),无法直接计算出区间内的答案
  2. 由上可知,如果想要维护出正确的答案,需要从叶子节点再自底向上 pushup 一次
  3. 那么总时间复杂度为 n^2logn,还不如直接 n*n 的暴力跑得快

于是可持久化线段树的区间合并就没办法了,也可能是我知识浅薄不会实现,抱着试一试的心态去百度了一下题解,发现这个题目真的可以用主席树来实现,只不过需要转换一下模型:

对于每个数字 x 来说,假设其只与前驱,也就是 x - 1 有关联,再假设当前如果有 num 个数,如果其中有 cnt 个数的前驱也在这 num 个数当中,那么这 cnt 个数都可以和前驱合并,对答案不做贡献,所以最后的不相交连续子段的个数是 num - cnt 个

这样问题就转换为了:对于某个节点来说,其子树中有多少个节点的前驱也在子树中,答案就是子树的大小与这个做差了

代码:

树上启发式合并

#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>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=2e5+100;vector<int>node[N];int ans[N],son[N],num[N],sum;bool vis[N],book[N];void dfs_son(int u,int fa)//树链剖分跑出重链
{son[u]=-1;num[u]=1;for(auto v:node[u]){if(v==fa)continue;dfs_son(v,u);num[u]+=num[v];if(son[u]==-1||num[v]>num[son[u]])son[u]=v;}
}void cal(int u,int fa,int val)//对于每个节点计算其子树的贡献
{if(val==1){book[u]=true;if(book[u-1]&&book[u+1])sum--;else if(book[u-1]||book[u+1]);elsesum++;}else{book[u]=false;if(book[u-1]&&book[u+1])sum++;else if(book[u-1]||book[u+1]);elsesum--;}for(auto v:node[u]){if(v==fa||vis[v])continue;cal(v,u,val);}
}void dfs(int u,int fa,int keep)//启发式合并
{for(auto v:node[u]){if(v==fa||v==son[u])continue;dfs(v,u,0);}if(son[u]!=-1){dfs(son[u],u,1);vis[son[u]]=true;}cal(u,fa,1);ans[u]=sum;if(son[u]!=-1)vis[son[u]]=false;if(!keep)cal(u,fa,-1);
}void init(int n)
{for(int i=1;i<=n;i++)node[i].clear();memset(vis,false,n+5);memset(book,false,n+5);sum=0;
}int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);int w;cin>>w;int kase=0;while(w--){int n;scanf("%d",&n);init(n);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_son(1,-1);dfs(1,-1,1);printf("Case #%d:",++kase);for(int i=1;i<=n;i++)printf(" %d",ans[i]);puts("");}return 0;
}

主席树

#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>
using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=2e5+100;
/*主席树*/
struct Node
{int l,r;int sum;
}tree[N*20];int cnt,root[N];void update(int pos,int &k,int l,int r)
{if(pos<l||pos>r)return;tree[cnt++]=tree[k];k=cnt-1;tree[k].sum++;if(l==r)return;int mid=l+r>>1;if(pos<=mid)update(pos,tree[k].l,l,mid);elseupdate(pos,tree[k].r,mid+1,r);
}int query(int i,int j,int l,int r,int L,int R)//[l,r]:目标区间,[L,R]:当前区间
{if(R<l||L>r)return 0;if(L>=l&&R<=r)return tree[j].sum-tree[i].sum;int mid=L+R>>1;return query(tree[i].l,tree[j].l,l,r,L,mid)+query(tree[i].r,tree[j].r,l,r,mid+1,R);
}
/*主席树*/
/*dfs序*/
vector<int>node[N];int L[N],R[N],tot,id[N],sz[N]; void dfs(int u,int fa)
{sz[u]=1;L[u]=++tot;id[tot]=u;for(auto v:node[u]){if(v==fa)continue;dfs(v,u);sz[u]+=sz[v];}R[u]=tot;
}
/*dfs序*/
void init(int n)
{root[0]=0;tree[0].l=tree[0].r=tree[0].sum=0;cnt=1;tot=0;for(int i=1;i<=n;i++)node[i].clear();
}int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);int w;cin>>w;int kase=0;while(w--){int n;scanf("%d",&n);init(n);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,-1);for(int i=1;i<=n;i++)//遍历dfs序建树 {root[i]=root[i-1];update(L[id[i]-1],root[i],1,n);//第i个dfs序表示的节点是id[i],其前驱为id[i]-1,将其前驱的dfs序标记一下}printf("Case #%d:",++kase);for(int i=1;i<=n;i++)printf(" %d",sz[i]-query(root[L[i]-1],root[R[i]],L[i],R[i],1,n));//查找时只需要查找子树中的点的前驱个数即可puts("");}return 0;
}

中石油训练赛 - Russian Dolls on the Christmas Tree(树上启发式合并/主席树)相关推荐

  1. 中石油训练赛 - One-Way Conveyors(边双缩点+树上差分)

    题目链接:点击查看 题目大意:给出一张 n 个点 m 条边的无向图,现在需要将这张图转换为有向图,并且使得 k 个可达条件成立,输出一种构造方案 题目分析:如果在无向图中出现环的话,那么在转换为有向图 ...

  2. Russian Dolls on the Christmas Tree

    题目链接:Russian Dolls on the Christmas Tree 显然可以dfs序转区间之后上莫队,不过感觉会被卡. 其实也可以dfs序之后,对询问区间排序,然后线段树. 我懒得dfs ...

  3. 中石油训练赛 - 小A盗墓(线段树+异或结论)

    题目链接:点击查看 题目大意:给出n个数,以及m个操作,每个操作分为两种: 1 x y:将第x个数变为y 2 x y:判断闭区间[x,y]中的数能否在进行升序排序后构成一段连续且上升序列 题目分析:因 ...

  4. Russian Dolls on the Christmas Tree(主席树)

    传送门 题意:给你一颗树,每个节点都有一个标号,问你将一个节点所有子节点及其本身的标号连接能形成几段. 题解:大多是用线段树合并写的,所以补一发主席树的做法.将树用dfs序hash下来后,对于每一个节 ...

  5. 中石油训练赛 - Trading Cards(最大权闭合子图)

    题目大意:给出 n 个卡片,可以自由买卖,且价格都是相同的,再给出 m 个集合,如果已经得到了其中一个集合中的卡片,那么可以获得该集合的收益,问如何操作可以使得收益最大化 题目分析:最大权闭合子图的模 ...

  6. 中石油训练赛 - Watch Later(状压dp)

    题目链接:点击查看 题目大意: 给出一个长度为 n 的字符串,字符串中共有 k 种不同的字符,现在问删除掉所有字符的最小操作数,对于每种字符需要确定一个先后顺序,每次需要删除掉当前所有的这种字符才能去 ...

  7. 中石油训练赛 - Swapping Places(字典序最小的拓扑排序)

    题目链接:点击查看 题目大意:给出 s 个字符串表示种类,再给出 m 个朋友关系,表示两个种类的动物是朋友,现在给出一个长度为 n 的种类排列,规定相邻两个是朋友的种类的动物可以交换位置,问如何操作, ...

  8. 中石油训练赛 - Gone Fishing(固定大小的圆可以覆盖最多的点)

    题目大意:在二维平面中给出 n 个点,再给出一个固定大小的圆,问如何放置这个圆可以使其覆盖最多的点 题目分析:首先不难想到一种 n^3 的做法,就是两层循环去枚举两个点,因为两个不同的点就可以确定下来 ...

  9. 中石油训练赛 - Check List(线段树维护偏序问题)

    题目大意:给出 n 个点,需要计算出满足下列条件的三元对 ( i , j , k ) 的数量: x[ i ] < x[ j ] < x[ k ] y[ k ] > y[ i ] &g ...

最新文章

  1. Design Pattern - Singleton(C#)
  2. 【SpringBoot】在SpringBoot中使用Ehcache
  3. 设置Linux网络的方法
  4. 《给教师的阅读建议》经典语录
  5. aop注解配置切点 spring_springboot aop 自定义注解方式实现一套完善的日志记录
  6. 【中山市选2009】【BZOJ2463】谁能赢呢?
  7. Android 中Animation简单例子
  8. ubnt虚拟服务器端口转发,ubnt详细设置教程
  9. 『动态规划』矩阵连乘
  10. python考勤系统代码_500行python代码打造刷脸考勤系统
  11. 社交网络算法对权力的游戏角色进行分析
  12. 二极管整流电路工作原理图
  13. Java响应式编程基础-响应式流
  14. Xshell_5安装与使用
  15. 中蜂几月份自然分蜂_中蜂养殖,如何给蜜蜂分蜂?时间是关键
  16. 2个硬盘离线导致raid崩溃的数据恢复案例
  17. 2021年塔式起重机司机模拟考试题及塔式起重机司机作业考试题库
  18. TCP模拟HTTP发送get和post请求
  19. TWaver自动化设计平台Legolas —— 数据响应动作
  20. 下载谷歌play应用_选择在现有应用中使用Google Play应用签名

热门文章

  1. REDIS故障排查工具
  2. Nacos OPEN API配置管理测试
  3. 数据库存储数据的特点
  4. 存储过程语法 - 变量
  5. 分布式ID-雪花算法
  6. consumer和partition的数量建议
  7. MyBatis 缓存详解-一级缓存的不足
  8. 双主双从(2m-2s)集群介绍和工作流程说明
  9. ES6新特性之修饰器
  10. Response_功能介绍