【洛谷P2486】【BZOJ2243】染色【树链剖分】

2024-01-30 09:58

本文主要是介绍【洛谷P2486】【BZOJ2243】染色【树链剖分】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

题目大意:

题目链接:
BZOJ:https://www.lydsy.com/JudgeOnline/problem.php?id=2243
洛谷:https://www.luogu.org/problem/P2486
给出一棵树,维护下列操作:

  • C a b c C\ a\ b\ c C a b c:把结点 a a a到结点 b b b路径上的全部结点染成 c c c颜色
  • Q a b Q\ a\ b Q a b:询问结点 a a a到结点 b b b的颜色段数量。

思路:

把一个区间染色就是给线段树上的区间打一个标记,而询问颜色数量也基本来说就是一个普通的查询。所以可以考虑使用树剖。
但是我们发现我们在把树分成一条条的链后,原本的相邻两个点本来可能是相同颜色的,但是剖开之后就分成了两组询问,每组询问都会把这种颜色算一次,但是实际上这一段颜色是只能算一次的。
例如下图
在这里插入图片描述
红色的边为重边,此时询问 x , y x,y x,y之间的颜色段数量,正确答案应该是3,但是我们将蓝点之间的边剖开之后,答案就变成了4。
所以我们需要记录上一条重链的 t o p top top的颜色,如果这个颜色和此时这一条重链的颜色相同,那么答案就要减1。
所以我们设一个 f i n d find find函数,用来查询某一个结点的颜色。然后由于是 x y xy xy两个点同时跳,所以我们要记录两条重链的信息。当 x y xy xy位于同一重链后需要进行两次判断。
时间复杂度 O ( n log ⁡ 2 n ) O(n\log^2 n) O(nlog2n)


代码:

为什么我的树剖这么长,同机房的dalao都才140,150行的亚子

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;const int N=100010;
int col[N],son[N],size[N],id[N],rk[N],top[N],fa[N],dep[N],head[N];
int n,m,cnt,tot;
char ch;struct Treenode
{int l,r,lcol,rcol,sum,lazy;
};struct edge
{int next,to;
}e[N*2];struct Tree
{Treenode tree[N*4];void pushup(int x){tree[x].sum=tree[x*2].sum+tree[x*2+1].sum;if (tree[x*2].rcol==tree[x*2+1].lcol) tree[x].sum--;tree[x].lcol=tree[x*2].lcol;tree[x].rcol=tree[x*2+1].rcol;}void pushdown(int x){if (tree[x].lazy){tree[x*2].lazy=tree[x*2+1].lazy=tree[x].lazy;tree[x*2].lcol=tree[x*2].rcol=tree[x*2+1].lcol=tree[x*2+1].rcol=tree[x].lazy;tree[x*2].sum=tree[x*2+1].sum=1;tree[x].lazy=0;}}void build(int x){if (tree[x].l==tree[x].r){tree[x].sum=1;tree[x].lcol=tree[x].rcol=col[rk[tree[x].l]];return;}int mid=(tree[x].l+tree[x].r)>>1;tree[x*2].l=tree[x].l;tree[x*2].r=mid;tree[x*2+1].l=mid+1;tree[x*2+1].r=tree[x].r;build(x*2); build(x*2+1);pushup(x);}void update(int x,int l,int r,int val){if (tree[x].l==l && tree[x].r==r){tree[x].sum=1;tree[x].lcol=tree[x].rcol=tree[x].lazy=val;return;}pushdown(x);int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) update(x*2,l,r,val);else if (l>mid) update(x*2+1,l,r,val);else update(x*2,l,mid,val),update(x*2+1,mid+1,r,val);pushup(x);}int ask(int x,int l,int r){if (tree[x].l==l && tree[x].r==r) return tree[x].sum;pushdown(x);int mid=(tree[x].l+tree[x].r)>>1;if (r<=mid) return ask(x*2,l,r);if (l>mid) return ask(x*2+1,l,r);int ans1=ask(x*2,l,mid),ans2=ask(x*2+1,mid+1,r);if (tree[x*2].rcol==tree[x*2+1].lcol) return ans1+ans2-1;else return ans1+ans2;}int find(int x,int p){if (tree[x].l==p && tree[x].r==p) return tree[x].lcol;pushdown(x);int mid=(tree[x].l+tree[x].r)>>1;if (p<=mid) return find(x*2,p);else return find(x*2+1,p);}
}Tree;void add(int from,int to)
{e[++tot].to=to;e[tot].next=head[from];head[from]=tot;
}void dfs1(int x,int f)
{fa[x]=f;dep[x]=dep[f]+1;size[x]=1;for (int i=head[x];~i;i=e[i].next){int y=e[i].to;if (y!=f){dfs1(y,x);size[x]+=size[y];if (size[y]>size[son[x]]) son[x]=y;}}
}void dfs2(int x,int tp)
{top[x]=tp;id[x]=++cnt;rk[cnt]=x;if (son[x]) dfs2(son[x],tp);for (int i=head[x];~i;i=e[i].next){int y=e[i].to;if (y!=fa[x] && y!=son[x]) dfs2(y,y);}
}void addrange(int x,int y,int k)
{while (top[x]!=top[y]){if (dep[top[x]]<dep[top[y]]) swap(x,y);Tree.update(1,id[top[x]],id[x],k);x=fa[top[x]];}if (id[x]>id[y]) Tree.update(1,id[y],id[x],k);else Tree.update(1,id[x],id[y],k);
}int ask(int x,int y)
{int ans=0,last[2]={-1,-1},p=0;while (top[x]!=top[y]){if (dep[top[x]]<dep[top[y]]) swap(x,y),p^=1;  //记得交换两条链的编号ans+=Tree.ask(1,id[top[x]],id[x]);if (last[p]==Tree.find(1,id[x])) ans--;  //判断颜色是否相同last[p]=Tree.find(1,id[top[x]]);x=fa[top[x]];}if (dep[x]>dep[y]) swap(x,y),p^=1;ans+=Tree.ask(1,id[x],id[y]);if (last[p^1]==Tree.find(1,id[y])) ans--;if (last[p]==Tree.find(1,id[x])) ans--;  //两条链都要判断return ans;
}int main()
{memset(head,-1,sizeof(head));scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)scanf("%d",&col[i]);for (int i=1,x,y;i<n;i++){scanf("%d%d",&x,&y);add(x,y); add(y,x);}dfs1(1,0); dfs2(1,1);Tree.tree[1].l=1; Tree.tree[1].r=n;Tree.build(1);int x,y,z;while (m--){while (ch=getchar()) if (ch=='C'||ch=='Q') break;if (ch=='C'){scanf("%d%d%d",&x,&y,&z);addrange(x,y,z);}else{scanf("%d%d",&x,&y);printf("%d\n",ask(x,y));}}return 0;
}

这篇关于【洛谷P2486】【BZOJ2243】染色【树链剖分】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/659868

相关文章

高精度计算(代码加解析,洛谷p1601,p1303)除法待更新

目录 高精度加法 高精度减法 高精度乘法 高精度加法 我们知道在c++语言中任何数据类型都有一定的表示范围。当两个被加数很大时,正常加法不能得到精确解。在小学,我们做加法都采用竖式方法。那么我们也只需要按照加法进位的方式就能得到最终解。 8 5 6+ 2 5 5-------1 1 1 1 加法进位: c[i] = a[i] + b[i];if(c[i] >=

洛谷 凸多边形划分

T282062 凸多边形的划分 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 先整一个半成品,高精度过两天复习一下补上 #include <iostream>#include <algorithm>#include <set>#include <cstring>#include <string>#include <vector>#include <map>

能量项链,洛谷

解释:  环形dp问题还是考虑将环拉直,可以参考我上一篇文章:环形石子合并 [2 3 5 10 2] 3 5 10 将环拉直,[]内是一个有效的区间,可以模拟吸收珠子的过程,         如[2 3 5] <=>(2,3)(3,5)    2是头,3是中间,5是尾 len >= 3:因为最后[2 10 2]是最小的可以合并的有效区间 len <= n + 1因为[2 3

洛谷P5490扫描线

0是最小的数字,将一个线段看成一个区间,对于一个矩形,从下扫到上,入边为1,而出边为-1,意思是将这个区间上的所有点加1(区间修改).把线段表示为Line[i],其中记录了l,r,h,tag,左右端点,高度,入边还是出边(1或-1) 那么每次区间修改后不为0的区间它的值可能是1,2,3或者是其它数字,这不好统计,可以将它转化一下,0是不是表示没有被覆盖过的地方,我们只要统计0的个数然后用总长减去

树链剖分 + 后缀数组 - E. Misha and LCP on Tree

E. Misha and LCP on Tree  Problem's Link   Mean:  给出一棵树,每个结点上有一个字母。每个询问给出两个路径,问这两个路径的串的最长公共前缀。 analyse: 做法:树链剖分+后缀数组. 记录每条链的串,正反都需要标记,组成一个长串. 然后记录每条链对应的串在大串中的位置,对大串求后缀数组,最后询问就是在一些链

【HDU】5029 Relief grain 树链剖分+离线标记法

传送门:【HDU】5029 Relief grain 题目分析:这题的方法太美妙了! 首先看到这是一颗树,那么很容易想到树链剖分。然后想到可以将查询排个序,然后每一种查询执行完以后更新每个点的最优值。但是这样进行的复杂度太高!尤其是当z给的没有一样的时候尤其如此。 那么我们是否可以找到更加高效的算法? 答案是肯定的! 先简化一下问题,如果这些操作是在线段上进行的,我们怎么求解?

【HDU】5221 Occupation【树链剖分】

传送门:【HDU】5221 Occupation 题目分析: 最直接的想法,用一棵树链剖分维护路径,一棵dfs序线段树维护子树。因为每次最多修改一个点,所以修改的时候我们暴力修改每个点就可以了。 my  code: my~~code: #pragma comment(linker, "/STACK:102400000,102400000")#include <stdio.h>#in

【HDU】5436 Transmigration tree【树链剖分+dp+rmq】

题目链接:【HDU】5436 Transmigration tree #pragma comment(linker, "/STACK:16777216")#include <stdio.h>#include <string.h>#include <vector>#include <algorithm>using namespace std ;typedef long long LL ;

【HDU】5574 Colorful Tree【子树染色,询问子树颜色数——线段树+bit+lca+set】

题目链接:【HDU】5574 Colorful Tree 题目大意:对一个子树染色,询问一个子树的颜色数。 题目分析: set set维护每种颜色所在的 dfs dfs序区间,修改均摊 nlogn nlogn。 #include <bits/stdc++.h>using namespace std ;typedef long long LL ;typedef pair < int , i

【HDU】5566 Clarke and room【树链剖分+AC自动机】

题目链接:Clarke and room #include <bits/stdc++.h>using namespace std ;typedef long long LL ;#define ls ( o << 1 )#define lson ls , l , m#define rs ( o << 1 | 1 )#define rson rs , m + 1 , r#define rt