Codeforces 题目传送门 & 洛谷题目传送门

看到集合的合并,可以本能地想到并查集。

不过这题的操作与传统意义上的并查集不太一样,传统意义上的并查集一般是用来判断连通性的,而此题还需支持赋值、集合内整体加等操作,似乎就有些难处理。

我们不妨考虑此题的弱化版:没有第二类集合的版本。也就是要求支持合并两个集合,集合整体加某个数 \(x\),单点查询三个操作。

很明显集合整体加某个数 \(x\) 就相当于在并查集根节点处打一个 \(+x\) 的标记,查询就暴力跳父亲求出待询问点到根节点路径上所有点的标记之和。不过稍微想想就知道这个做法是假的,因为如果直接采用按秩合并会导致标记加的点集出现问题,举个例子,比方说我们要合并 \(x,y\) 两个集合,其中 \(x\) 为父亲,我们假设原本 \(x\) 点上有一个 \(+v\) 的标记,显然这个标记所管辖的点的集合是原本 \(x\) 所在的连通块,但经过我们这么一合并,我们默认每个标记管辖的点的集合是其子树,这也就意味着原本 \(y\) 所在的集合也被错误地打上了 \(+v\) 的标记,这样显然会出问题。我们考虑换个合并的思路,每次合并建立一个虚点 \(z\),并让 \(x,y\) 的父亲都为 \(z\),这样就能保证每次标记管辖的点集是正确的了。但这样还会导致一个问题,那就是树的深度可能很大,暴力条父亲会 TLE。故考虑把操作离线下来,先建出最后的森林出来并求出 DFS 序,每次修改用线段树在待修改点的子树内整体加 \(v\),查询就执行一次线段树上的单点查询即可,很明显森林里的点数最多为 \(n+q\),故总复杂度 \((n+q)\log(n+q)\)。

接下来考虑有第二类集合的版本,这个子树清空貌似维护起来有些困难,不过我们考虑换个思路,使用差分的思想,询问某个点 \(x\) 的值可以转化为此时 \(x\) 点的权值减去上一次 \(x\) 点被清空前 \(x\) 点的权值。故我们只用求出每次询问,带询问点上一次被清空的时间即可。那么这个东西怎么求呢?其实与弱化版维护的思路大同小异,先离线建出第二类集合的森林出来,每次清空一个节点 \(x\) 就相当于在 \(x\) 子树内所有点赋值 \(t\)(其中 \(t\) 为此次操作的时间),表示这些点上一次被清空是时间 \(t\),这个可以用区间赋值、单点查询的线段树维护。

到这里题目基本上已经做完了,不妨理理思路。我们先将询问离线下来,建出两类集合并查集所形成的森林,并对两个森林各进行一遍 DFS 求出每个点的 DFS 序,第二次扫描全部与第二类集合有关的操作,对于区间清空的操作就在子树对应 DFS 序的区间内执行区间赋值操作,如果遇到询问就进行一遍线段树上的单点查询,记录下待询问点上一次被清空的时间。第三次扫描全部与第一类集合有关的操作,对于区间加的操作就在子树对应 DFS 序的区间内执行区间加操作,如果遇到询问就进行一遍线段树上的单点查询,这样就可以求出答案了。

时间复杂度 \((n+q)\log(n+q)\)。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=5e5;
int n,qu;
struct solver{
int bel[MAXN+5],siz[MAXN*2+5],tot;
void init(){
for(int i=1;i<=n;i++) bel[i]=i,siz[i]=1;
tot=n;
}
int hd[MAXN*2+5],nxt[MAXN*2+5],to[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
void merge(int x,int y){
tot++;adde(tot,bel[x]);adde(tot,bel[y]);
siz[x]+=siz[y];siz[y]=0;bel[y]=0;bel[x]=tot;
}
int dfn[MAXN*2+5],sz[MAXN*2+5],tim=0;
void dfs(int x){
dfn[x]=++tim;sz[x]=1;
for(int e=hd[x];e;e=nxt[e]) dfs(to[e]),sz[x]+=sz[to[e]];
}
} s1,s2;
struct event{int opt,x,y;} q[MAXN+5];
vector<int> clr[MAXN+5];
ll res[MAXN+5];
struct seg1{
struct node{int l,r;ll v,lz;} s[MAXN*8+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushdown(int k){
if(s[k].lz){
s[k<<1].v+=s[k].lz*(s[k<<1].r-s[k<<1].l+1);
s[k<<1].lz+=s[k].lz;
s[k<<1|1].v+=s[k].lz*(s[k<<1|1].r-s[k<<1|1].l+1);
s[k<<1|1].lz+=s[k].lz;
s[k].lz=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){
s[k].v+=1ll*x*(s[k].r-s[k].l+1);s[k].lz+=x;
return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
s[k].v=s[k<<1].v+s[k<<1|1].v;
}
ll query(int k,int x){
if(s[k].l==s[k].r) return s[k].v;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(x<=mid) return query(k<<1,x);
else return query(k<<1|1,x);
}
} st1;
struct seg2{
struct node{int l,r,tag;} s[MAXN*8+5];
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void pushdown(int k){
if(s[k].tag){
s[k<<1].tag=s[k].tag;s[k<<1|1].tag=s[k].tag;
s[k].tag=0;
}
}
void modify(int k,int l,int r,int x){
if(l<=s[k].l&&s[k].r<=r){s[k].tag=x;return;}
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r,x);
else if(l>mid) modify(k<<1|1,l,r,x);
else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
}
int query(int k,int x){
if(s[k].l==s[k].r) return s[k].tag;
pushdown(k);int mid=s[k].l+s[k].r>>1;
if(x<=mid) return query(k<<1,x);
else return query(k<<1|1,x);
}
} st2;
int main(){
scanf("%d%d",&n,&qu);s1.init();s2.init();
for(int i=1;i<=qu;i++){
char opt[5];scanf("%s",opt+1);
if(opt[1]=='U'){
int x,y;scanf("%d%d",&x,&y);s1.merge(x,y);
q[i].opt=1;q[i].x=x;q[i].y=y;
} else if(opt[1]=='M'){
int x,y;scanf("%d%d",&x,&y);s2.merge(x,y);
q[i].opt=2;q[i].x=x;q[i].y=y;
} else if(opt[1]=='A'){
int x;scanf("%d",&x);
q[i].opt=3;q[i].x=s1.bel[x];q[i].y=s1.siz[x];
} else if(opt[1]=='Z'){
int x;scanf("%d",&x);
q[i].opt=4;q[i].x=s2.bel[x];
} else{
int x;scanf("%d",&x);
q[i].opt=5;q[i].x=x;
}
}
s1.tot++;for(int i=1;i<=n;i++) if(s1.siz[i]) s1.adde(s1.tot,s1.bel[i]);
s2.tot++;for(int i=1;i<=n;i++) if(s2.siz[i]) s2.adde(s2.tot,s2.bel[i]);
s1.dfs(s1.tot);s2.dfs(s2.tot);
st1.build(1,1,s1.tot);st2.build(1,1,s2.tot);
for(int i=1;i<=qu;i++){
if(q[i].opt==4) st2.modify(1,s2.dfn[q[i].x],s2.dfn[q[i].x]+s2.sz[q[i].x]-1,i);
if(q[i].opt==5){
int tmp=st2.query(1,s2.dfn[q[i].x]);
if(tmp) clr[tmp].pb(i);
}
}
for(int i=1;i<=qu;i++){
if(q[i].opt==3) st1.modify(1,s1.dfn[q[i].x],s1.dfn[q[i].x]+s1.sz[q[i].x]-1,q[i].y);
if(q[i].opt==4){
for(int j=0;j<clr[i].size();j++){
int id=clr[i][j];
res[id]-=st1.query(1,s1.dfn[q[id].x]);
}
}
if(q[i].opt==5){
res[i]+=st1.query(1,s1.dfn[q[i].x]);
printf("%lld\n",res[i]);
}
}
return 0;
}

Codeforces 571D - Campus(并查集+线段树+DFS 序,hot tea)的更多相关文章

  1. cf1278D——树的性质+并查集+线段树/DFS判环

    昨天晚上本来想认真打一场的,,结果陪女朋友去了.. 回来之后看了看D,感觉有点思路,结果一直到现在才做出来 首先对所有线段按左端点排序,然后用并查集判所有边是否联通,即遍历每条边i,和前一条不覆盖它的 ...

  2. BZOJ3545 [ONTAK2010]Peaks kruskal 并查集 主席树 dfs序

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3545 题意概括 Description 在Bytemountains有N座山峰,每座山峰有他的高度 ...

  3. BZOJ3551 [ONTAK2010]Peaks加强版 kruskal 并查集 主席树 dfs序

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ3551 题意概括 Description 在Bytemountains有N座山峰,每座山峰有他的高度 ...

  4. UVA1455 - Kingdom(并查集 + 线段树)

    UVA1455 - Kingdom(并查集 + 线段树) 题目链接 题目大意:一个平面内,给你n个整数点,两种类型的操作:road x y 把city x 和city y连接起来,line fnum ...

  5. 并查集&线段树&树状数组&排序二叉树

    超级无敌巨牛逼并查集(带权并查集)https://vjudge.net/problem/UVALive-4487 带删点的加权并查集 https://vjudge.net/problem/UVA-11 ...

  6. 【Codeforces576E_CF576E】Painting Edges(可撤销并查集+线段树分治)

    题目 CF576E 分析: 从前天早上肝到明天早上qwq其实颓了一上午MC ,自己瞎yy然后1A,写篇博客庆祝一下. 首先做这题之前推荐一道很相似的题:[BZOJ4025]二分图(可撤销并查集+线段树 ...

  7. BZOJ 3910 并查集+线段树合并

    思路: 1. 并查集+线段树合并 记得f[LCA]==LCA的时候 f[LCA]=fa[LCA] 2.LCT(并不会写啊...) //By SiriusRen #include <cstdio& ...

  8. Tsinsen A1505. 树(张闻涛) 倍增LCA,可持久化线段树,DFS序

    题目:http://www.tsinsen.com/A1505 A1505. 树(张闻涛) 时间限制:1.0s   内存限制:512.0MB    总提交次数:196   AC次数:65   平均分: ...

  9. BZOJ_3252_攻略_线段树+dfs序

    BZOJ_3252_攻略_线段树+dfs序 Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏.今天他得到了一款新游戏< ...

随机推荐

  1. iOS路由最佳选择是什么

    背景 记得四年前iOS路由开始盛行,当时比较有名的是蘑菇街的,后来CTMediator写了几篇文章把蘑菇街批的体无完肤,导致我后来写新项目用了CTMediator,那一堆组件创建的叫一个酸爽啊!再后来 ...

  2. Coursera Deep Learning笔记 改善深层神经网络:超参数调试 正则化以及梯度相关

    笔记:Andrew Ng's Deeping Learning视频 参考:https://xienaoban.github.io/posts/41302.html 参考:https://blog.cs ...

  3. js--数组的 fill() 填充方法详解

    前言 我们知道了很多了初始化数组的方法,但是初始化数组之后,数组中的每一项元素默认为 empty 空位占位,如何对数组这些空位添加默认的元素,ES6提供了 fill() 方法实现这一操作.本文总结数组 ...

  4. 数列极限计算中运用皮亚诺Taylor展开巧解

    这是讲义里比较精华的几个题目,今晚翻看也是想到了,总结出来(处理k/n2形式). 推广式子如下: 例题如下:

  5. 攻防世界 web3.backup

    如果网站存在备份文件,常见的备份文件后缀名有:.git ..svn..swp..~..bak..bash_history..bkf尝试在URL后面,依次输入常见的文件备份扩展名. ip/index.p ...

  6. 【做题记录】max-min+1=len 区间计数

    (来源:XJ高质量原创题) 原题地址 弱化版:CF526F Pudding Monsters 弱化版 题意:\(n\times n\) 的棋盘上有 \(n\) 颗棋子,每行每列都有且仅有一颗棋子,求出 ...

  7. 『学了就忘』Linux基础 — 8、虚拟机网络模式说明

    目录 1.虚拟机网卡 2.网络连接模式对应工作的网卡 3.桥接模式说明 4.补充说明 这篇主要总结一下虚拟机网络配置中桥接模式.NAT模式和仅主机模式的区别. 打开VMware,选中虚拟机,点击网络适 ...

  8. PWN学习之栈溢出

    目录 PWN学习之栈溢出 前言 写bug bug.cpp源码 OD动态调试bug.exe OD调试观察溢出 栈溢出攻击之突破密码验证 x64位栈溢出 PWN学习之栈溢出 前言 我记得我在最开始学编程的 ...

  9. 修改 openssh 版本号

    1.查看 sshd 位置 #which sshd 2.查看 /usr/sbin/sshd(二进制文件) 内容 #strings /usr/sbin/sshd | grep nicai 3.修改版本号, ...

  10. CentOS部署多台服务器JDK(shell脚本部署)

    部署7台新服务器的jdk,数量不算多,但也不打算一台一台的部署,写了个脚本执行 [ #!/bin/bash# JDK 安装包名jdk_packge="jdk-8u162-linux-x64. ...