【算法竞赛学习笔记】[SDOI2011]染色(路径染色+色段查询)

2023-11-05 06:32

本文主要是介绍【算法竞赛学习笔记】[SDOI2011]染色(路径染色+色段查询),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


title : [SDOI2011]染色(路径染色+色段查询)
tags : ACM,数据结构
date : 2021-11-16
author : Linno


P2486 [SDOI2011]染色

题面

给一棵树,支持两种操作:

①给定u,v,w,将u到v的路径上所有节点染成颜色w。

②给定u,v,查询u到v路径上颜色段的数量。

树链剖分+线段树

树链剖分将无根树拍平后,用线段树去做。维护每个区间的左颜色、右颜色、色段和以及tag,如果合并的时候两端颜色一致,那么ans-1。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;
int n,m,col[N];vector<int>G[N];
void addedge(int u,int v){G[u].push_back(v);G[v].push_back(u);
}int sz[N],dep[N],son[N],fa[N],bel[N],dfn[N],idfn[N],idx=0;
inline void dfs1(int x){sz[x]=1;for(int i=0;i<G[x].size();i++){int to=G[x][i];if(to==fa[x]) continue;dep[to]=dep[x]+1;fa[to]=x;dfs1(to);sz[x]+=sz[to];if(sz[son[x]]<sz[to]) son[x]=to;}
}inline void dfs2(int x,int tp){ //重链剖分dfn[x]=++idx;idfn[idx]=x;bel[x]=tp;if(son[x]) dfs2(son[x],tp);for(int i=0;i<G[x].size();i++){int to=G[x][i];if(to==fa[x]||to==son[x]) continue;dfs2(to,to);}
}struct Node{int sum,l,r,lc,rc,tg,c; 
}tr[N<<2];#define lss p<<1
#define rss p<<1|1inline void build(int p,int l,int r){tr[p].l=l;tr[p].r=r;if(l==r){tr[p].sum=1;tr[p].c=tr[p].lc=tr[p].rc=col[idfn[l]];return;}int mid=(l+r)>>1;build(lss,l,mid);build(rss,mid+1,r);tr[p].sum=(tr[lss].sum+tr[rss].sum-(tr[lss].rc==tr[rss].lc)); tr[p].lc=tr[lss].lc;tr[p].rc=tr[rss].rc;
}inline void pushdown(int p){ if(tr[p].l==tr[p].r) return;if(tr[p].tg){tr[lss].tg=tr[rss].tg=tr[p].tg;tr[lss].c=tr[rss].c=tr[p].tg;tr[lss].lc=tr[rss].lc=tr[p].tg;tr[lss].rc=tr[rss].rc=tr[p].tg;tr[lss].sum=tr[rss].sum=1;tr[p].tg=0;}
}inline void update(int p,int ql,int qr,int c){ //区间更新pushdown(p);if(ql==tr[p].l&&tr[p].r==qr){tr[p].sum=1;tr[p].tg=c;tr[p].c=tr[p].lc=tr[p].rc=c;return;}int mid=(tr[p].l+tr[p].r)>>1;if(ql>mid) update(rss,ql,qr,c);else if(qr<=mid) update(lss,ql,qr,c);else{update(lss,ql,mid,c);update(rss,mid+1,qr,c);}tr[p].sum=tr[lss].sum+tr[rss].sum-(tr[lss].rc==tr[rss].lc);tr[p].lc=tr[lss].lc;tr[p].rc=tr[rss].rc;
}inline int query(int p,int ql,int qr){ //区间查询pushdown(p);if(ql==tr[p].l&&tr[p].r==qr) return tr[p].sum;int mid=(tr[p].l+tr[p].r)>>1;if(mid<ql) return query(rss,ql,qr);else if(mid>=qr) return query(lss,ql,qr);else{return (query(lss,ql,mid)+query(rss,mid+1,qr)-(tr[lss].rc==tr[rss].lc));}	
}int Qc(int p,int ql,int qr){ //返回某位置的颜色pushdown(p);if(tr[p].l==ql&&tr[p].r==qr) return tr[p].c;int mid=tr[p].l+tr[p].r>>1;if(mid<ql) return Qc(rss,ql,qr);else return Qc(lss,ql,qr); 
}signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int u,v,w;char op;cin>>n>>m;for(int i=1;i<=n;i++) cin>>col[i];for(int i=1;i<n;i++){cin>>u>>v;addedge(u,v);}dfs1(1);dfs2(1,1); build(1,1,n);for(int i=1;i<=m;i++){cin>>op;if(op=='Q'){cin>>u>>v; //路径查询 int res=0,Cson,Cfa;while(bel[u]!=bel[v]){if(dep[bel[u]]<dep[bel[v]]) swap(u,v); res+=query(1,dfn[bel[u]],dfn[u]);Cson=Qc(1,dfn[bel[u]],dfn[bel[u]]);Cfa=Qc(1,dfn[fa[bel[u]]],dfn[fa[bel[u]]]);if(Cson==Cfa) res--; //合并链两端颜色相等u=fa[bel[u]];}if(dep[u]<dep[v]) swap(u,v);res+=query(1,dfn[v],dfn[u]);cout<<res<<"\n";}else{cin>>u>>v>>w;  //路径涂色 while(bel[u]!=bel[v]){if(dep[bel[u]]<dep[bel[v]]) swap(u,v); update(1,dfn[bel[u]],dfn[u],w);u=fa[bel[u]];}if(dep[u]<dep[v]) swap(u,v);update(1,dfn[v],dfn[u],w);}}return 0;
}

LCT

连接不同色点的边权值设为1,连接同色的点的边权值设为0,那么我们要输出的就是路径上所有的边权和+1。我们维护每个splay节点的最左端值和最右端值,对于它的父亲节点,就可以找到其前驱和后继的颜色,就可以累计节点跟前驱后继连边的权值和了。

#include<bits/stdc++.h>
#define int long long 
#define N 1000010
using namespace std;inline int read(){int x=0,o=0;char ch=getchar();while(ch!='-'&&(ch<'0'||'9'<ch)) ch=getchar();if(ch=='-') o=1,ch=getchar();while('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=getchar();return o?-x:x;
}
int n,m;
char ch;#define lson tr[x].ch[0]
#define rson tr[x].ch[1]
struct Splaytree{int w,c,lc,rc,tag,rev,fa,ch[2]; }tr[N];
int stk[N],top;inline bool isroot(int x) { return tr[tr[x].fa].ch[0]!=x&&tr[tr[x].fa].ch[1]!=x; }inline void pushdown(int x) {if(tr[x].rev) {swap(lson,rson);swap(tr[lson].lc,tr[lson].rc);swap(tr[rson].lc,tr[rson].rc);tr[lson].rev^=1,tr[rson].rev^=1;tr[x].rev=0;}if(tr[x].tag) {tr[x].lc=tr[x].rc=tr[x].c=tr[x].tag;tr[lson].tag=tr[rson].tag=tr[x].tag;tr[x].w=tr[x].tag=0;}
}inline void pushup(int x) {pushdown(lson),pushdown(rson);tr[x].w=tr[lson].w+tr[rson].w;if(lson) {tr[x].lc=tr[lson].lc;if(tr[x].c!=tr[lson].rc) ++tr[x].w;}else tr[x].lc=tr[x].c;if(rson) {tr[x].rc=tr[rson].rc;if(tr[x].c!=tr[rson].lc) ++tr[x].w;}else tr[x].rc=tr[x].c;
}inline void rotate(int x){int y=tr[x].fa,z=tr[y].fa;int k=tr[y].ch[1]==x;if(!isroot(y)) tr[z].ch[tr[z].ch[1]==y]=x;  tr[x].fa=z;tr[y].ch[k]=tr[x].ch[k^1],tr[tr[x].ch[k^1]].fa=y;tr[x].ch[k^1]=y,tr[y].fa=x;pushup(y);
}inline void splay(int x) {stk[top=1]=x;for(int i=x; !isroot(i); i=tr[i].fa) stk[++top]=tr[i].fa;while(top) pushdown(stk[top--]);while(!isroot(x)) {int y=tr[x].fa,z=tr[y].fa;if(!isroot(y))(tr[y].ch[0]==x)^(tr[z].ch[0]==y)?rotate(x):rotate(y);rotate(x);	}pushup(x);
}inline void access(int x) {for(int y=0; x; y=x,x=tr[x].fa) splay(x),rson=y,pushup(x);
}inline void makeroot(int x) {access(x),splay(x),tr[x].rev^=1;
}inline int findroot(int x){access(x),splay(x);while(lson) x=lson;return x;
}inline void split(int x,int y) {makeroot(x),access(y),splay(y);
}inline void link(int x,int y) {makeroot(x),tr[x].fa=y;
}signed main() {n=read();m=read();for(int i=1;i<=n;++i) tr[i].c=tr[i].lc=tr[i].rc=read();for(int u,v,i=1;i<n;++i) u=read(),v=read(),link(u,v); for(int a,b,c,i=1;i<=m;++i) {ch=getchar();while(ch!='C'&&ch!='Q') ch=getchar();if(ch=='C') {a=read(),b=read(),c=read();split(a,b);tr[b].tag=c;}if(ch=='Q') {a=read(),b=read();split(a,b);printf("%d\n",tr[b].w+1);}}return 0;
}

参考

https://www.luogu.com.cn/blog/user48618/solution-p2486

这篇关于【算法竞赛学习笔记】[SDOI2011]染色(路径染色+色段查询)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

mysql线上查询之前要性能调优的技巧及示例

《mysql线上查询之前要性能调优的技巧及示例》文章介绍了查询优化的几种方法,包括使用索引、避免不必要的列和行、有效的JOIN策略、子查询和派生表的优化、查询提示和优化器提示等,这些方法可以帮助提高数... 目录避免不必要的列和行使用有效的JOIN策略使用子查询和派生表时要小心使用查询提示和优化器提示其他常

MySQL9.0默认路径安装下重置root密码

《MySQL9.0默认路径安装下重置root密码》本文主要介绍了MySQL9.0默认路径安装下重置root密码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们... 目录问题描述环境描述解决方法正常模式下修改密码报错原因问题描述mysqlChina编程采用默认安装路径,

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

查询SQL Server数据库服务器IP地址的多种有效方法

《查询SQLServer数据库服务器IP地址的多种有效方法》作为数据库管理员或开发人员,了解如何查询SQLServer数据库服务器的IP地址是一项重要技能,本文将介绍几种简单而有效的方法,帮助你轻松... 目录使用T-SQL查询方法1:使用系统函数方法2:使用系统视图使用SQL Server Configu

MYSQL关联关系查询方式

《MYSQL关联关系查询方式》文章详细介绍了MySQL中如何使用内连接和左外连接进行表的关联查询,并展示了如何选择列和使用别名,文章还提供了一些关于查询优化的建议,并鼓励读者参考和支持脚本之家... 目录mysql关联关系查询关联关系查询这个查询做了以下几件事MySQL自关联查询总结MYSQL关联关系查询

Java实现Elasticsearch查询当前索引全部数据的完整代码

《Java实现Elasticsearch查询当前索引全部数据的完整代码》:本文主要介绍如何在Java中实现查询Elasticsearch索引中指定条件下的全部数据,通过设置滚动查询参数(scrol... 目录需求背景通常情况Java 实现查询 Elasticsearch 全部数据写在最后需求背景通常情况下