CF1416D 做题心得

上次在某trick中提到了这个题,一开始觉得太毒瘤没有写,现在把它补上了。

感觉实现这个东西,比单纯收获一个trick,收获的东西多太多了。

主要思路

它的主要trick是“反向反向操作日神仙”,也就是,先删掉所有边,反过来做一遍,然后再用撤销的方式正过来再做一遍。

思路的框架就是,先把边都删掉,然后做一个Kruskal重构树。Kruskal重构树有一个性质就是,每一个子树都和某时刻的某联通块对应。

而我们在删边的过程中,每一个点对应的联通块就相当于,先把所有边都删掉,然后把它后面的删边操作都加上去后,它所在的联通块。那这样每个点管一个后缀,容易想到反过来撤销删边操作(即加边)。在这个过程中我们可以知道每个点对应的联通块的根节点 \(anc\)(换句话说,Kruskal重构树最终形态上以 \(anc\) 为根的子树就是这个点当时的联通块)。

然后就相当于每次从一个子树中选最大值,并将其修改为0。在dfs序上线段树做,trival。

细节

注意我们每次要找到联通块的根节点,但是我们还不好轻易的改树形态(路径压缩),Kruskal重构树也没有按秩合并一说。那怎么优化呢?

开两个数组,一个路径压缩,一个不路径压缩,存真实的树。压缩的那个保证复杂度,不压缩的那个保证正确性。俩分开使用。

代码

#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 500005
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.st(u),v=G.to(i);~i;i=G.nx(i),v=G.to(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define all(a) a.begin(),a.end()
#define iter(a,p) (a.begin()+p)
int I() {char c=getchar(); int x=0; int f=1; while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar(); while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return ((f==1)?x:-x);}
template <typename T> void Rd(T& arg){arg=I();}
template <typename T,typename...Types> void Rd(T& arg,Types&...args){arg=I(); Rd(args...);}
void RA(int *p,int n) {F(i,1,n) *p=I(),++p;}
struct edge{int u,v;} e[N];
struct op{int t,x;} o[N];
int val[N];
int n,m,q;
void Input()
{
Rd(n,m,q);
F(i,1,n) val[i]=I();
F(i,1,m) e[i]=(edge){I(),I()};
F(i,1,q) o[i]=(op){I(),I()};
} class Union_Find
{
public:
int fa[N<<1],anc[N<<1];
// anc用来路径压缩优化时间, fa保留原树的形状, 保证后面求dfs序的正确性
// 然而这个trick很逊, 即使保持了原树的形状, 仍不能用于可撤销并查集 ———— 因为 anc 的修改太多了
int tot=0;
void clear(int n) {F(i,1,n+m) fa[i]=anc[i]=i; tot=n;}
int find(int x) {return x==anc[x]?x:anc[x]=find(anc[x]);}
void merge(int u,int v)
{
u=find(u),v=find(v);
if (u==v) return;
++tot; fa[u]=fa[v]=anc[u]=anc[v]=tot;
}
}un;
int anc[N];
class Graph
{
public:
int head[N];
struct node{int v,nx;} e[N]; int ecnt;
void clear() {MEM(head,-1); MEM(e,-1); ecnt=-1;}
void add(int u,int v)
{
e[++ecnt]=(node){v,head[u]}; head[u]=ecnt;
}
void add2(int u,int v) {add(u,v); add(v,u);}
int st(int u) {return head[u];}
int to(int i) {return e[i].v;}
int nx(int i) {return e[i].nx;}
}G;
bool vis[N];
int idfn[N],odfn[N],dfq[N],tick=0;
void DFS(int u) // dfq 存储访问节点的顺序, 里面存节点, 换句话说就是 dfn 的逆变换
{
++tick; dfq[tick]=u; idfn[u]=tick;
Tra(i,u) DFS(v);
odfn[u]=tick;
}
struct info{int pos,val;};
info operator+(info a,info b)
{
return a.val>b.val?a:b;
}
class SegmentTree
{
public:
info s[N<<2];
#define ls ix<<1
#define rs ix<<1|1
#define inx int ix=1,int L=1,int R=n+m
#define lson ls,L,mid
#define rson rs,mid+1,R
void up(int ix) {s[ix]=s[ls]+s[rs];}
void Build(inx)
{
s[ix].val=val[dfq[L]]; s[ix].pos=L;
if (L==R) return;
int mid=(L+R)>>1;
Build(lson); Build(rson); up(ix);
}
void Change(int pos,int val,inx)
{
if (L==R) {s[ix]=(info){pos,val}; return;}
int mid=(L+R)>>1;
if (pos<=mid) Change(pos,val,lson);
else Change(pos,val,rson);
up(ix);
}
info Query(int l,int r,inx)
{
if (l<=L and R<=r) return s[ix];
int mid=(L+R)>>1;
if (r<=mid) return Query(l,r,lson);
if (l>mid) return Query(l,r,rson);
return Query(l,mid,lson)+Query(mid+1,r,rson);
}
}T;
void Soviet()
{
// 先删除所有边, 然后反向遍历, 维护Kruskal重构树
// 途中预处理出每个查询节点的祖先(用当时的并查集做find), 同时把边加回来
// 然后再利用Kruskal重构树的结构, 正着做一遍 F(i,1,q) if (o[i].t==2) vis[o[i].x]=1;
un.clear(n);
F(i,1,m) if (!vis[i]) un.merge(e[i].u,e[i].v);
D(i,q,1)
{
int t=o[i].t,x=o[i].x;
if (t==2)
{
un.merge(e[x].u,e[x].v);
}
else
{
anc[i]=un.find(x);
}
}
G.clear();
F(i,1,un.tot) if (un.fa[i]!=i) G.add(un.fa[i],i);
F(i,1,un.tot) if (un.find(i)==i) DFS(i);
T.Build(); F(i,1,q)
{
if (o[i].t==1)
{
int f=anc[i];
info ans=T.Query(idfn[f],odfn[f]);
printf("%d\n",ans.val);
T.Change(ans.pos,0);
}
}
}
void IsMyWife()
{
Input();
Soviet();
}
}
#undef int //long long
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();
return 0;
}

CF1416D 做题心得的更多相关文章

  1. [JSOI2019]节日庆典 做题心得

    [JSOI2019]节日庆典 做题心得 一个性质有趣的字符串题 这要是在考场上我肯定做不出来吧 一开始还以为要 SAM 什么的暴力搞,没想到只用到了 \(Z\) 函数 -- 也是我生疏了罢 (学了啥忘 ...

  2. [BJOI2016]水晶 做题心得

    [BJOI2016]水晶 做题心得 这是一个写了我两小时的傻逼题.写这个题浪费了一堆时间后,我才意识到我码力又不行了.于是整理起了实现技巧,开始练码力. 思路 不难.首先把 \((x,y,z)\) 变 ...

  3. [NOIP补坑计划]NOIP2017 题解&做题心得

    终于做完了…… 场上预计得分:?(省一分数线:295) 由于看过部分题解所以没有预计得分qwq 题解: D1T1 小凯的疑惑 题面 震惊!一道小学奥数题竟难倒无数高中考生! 欢迎大家以各种姿势*和谐* ...

  4. [NOIP补坑计划]NOIP2016 题解&做题心得

    感觉16年好难啊QAQ,两天的T2T3是不是都放反了啊…… 场上预计得分:100+80+100+100+65+100=545(省一分数线280) ps:loj没有部分分,部分分见洛咕 题解: D1T1 ...

  5. [NOIP补坑计划]NOIP2015 题解&做题心得

    感觉从15年开始noip就变难了?(虽然自己都做出来了……) 场上预计得分:100+100+60~100+100+100+100=560~600(省一分数线365) 题解: D1T1 神奇的幻方 题面 ...

  6. [NOIP补坑计划]NOIP2013 题解&做题心得

    场上预计得分:100+100+100+100+100+60=560(省一分数线410) 五道傻逼题+一道大搜索题…… 题解: D1T1 转圈游戏 题面 水题送温暖~ #include<algor ...

  7. [NOIP补坑计划]NOIP2012 题解&做题心得

    场上预计得分:100+90+70+100+100+3060=490520(省一分数线245) 题解: D1T1 Vigenère 密码 题面 水题送温暖~~ #include<iostream& ...

  8. [NOIP补坑计划]NOIP2014 题解&做题心得

    六道普及组题,没啥好说的 场上预计得分:100+100+100+100+100+100=600(省一分数线490) (AK是不可能AK的,这辈子不可能AK的) 题解: D1T1 生活大爆炸版石头剪刀布 ...

  9. 标 题: [心得]传统IT转互联网面试经验分享

    发信人: lgonnet (逃之夭夭), 信区: Java标  题: [心得]传统IT转互联网面试经验分享发信站: 水木社区 (Wed Jul  1 10:18:38 2015), 站内 统一回复一下 ...

随机推荐

  1. mysql数据库连接java

    1 1.创建配置文件jdbc.properties 2 3 jdbc.driver=com.mysql.jdbc.Driver 4 jdbc.url=jdbc:mysql://localhost:33 ...

  2. SparkStreaming推测机制:面试被问遇到什么问题,说这个显水平!

    背景 老刘最近晚上会刷刷牛客网的大数据开发面经,总是会看到一个高频的面试题,那就是你在学习过程中遇到过什么问题吗? 这个问题其实有点难回答,如果我说的太简单了,会不会让面试官觉得水平太低,那我应该讲什 ...

  3. 第一章节 BJROBOT ROS 网络配置及移动控制【ROS全开源阿克曼转向智能网联无人驾驶车】

    版权声明:该教程版权归北京智能佳科技有限公司所有,未经公司授权禁止引用.发布.转载等,否则将追究其法律责任. 使用前说明:本使用文档说明略微简明,请结合指导视频进行操作会更容易理解!! 第一章节 BJ ...

  4. win8.1下jdk的安装和环境变量的配置 eclipse的安装和汉化

    1.首先下载jdk安装包,安装的时候会有两个文件安装,一个是jdk一个是jre建议两个文件不要安装在一个目录下 2.安装jdk后面就是配置环境变量,path和classpath,path要在用户变量中 ...

  5. 【递归】P1706全排列问题

    题目相关 题目描述 输出自然数 1 到 n所有不重复的排列,即 n的全排列,要求所产生的任一数字序列中不允许出现重复的数字. 输入格式 一个整数 n**. 输出格式 由 1∼n 组成的所有不重复的数字 ...

  6. JVM-Class文件的结构

    Class类文件的结构 Class文件是一株以8个字节为单位的二进制流.各个数据项目严格按照顺序紧凑的排列在文件之中,中间没有任何的分隔符,当遇到占用的空间大于8个字节时,会按照高位在前的方式进行分割 ...

  7. 【Azure Developer】Python代码通过AAD认证访问微软Azure密钥保管库(Azure Key Vault)中机密信息(Secret)

    关键字说明 什么是 Azure Active Directory?Azure Active Directory(Azure AD, AAD) 是 Microsoft 的基于云的标识和访问管理服务,可帮 ...

  8. requests顺序执行实现

    多步请求封装,执行完一个用例 def requests(self,step_info): request_type =step_info['请求方式'] if request_type==" ...

  9. 【Azure Developer】解决Azure Key Vault管理Storage的示例代码在中国区Azure遇见的各种认证/授权问题 - C# Example Code

    问题描述 使用Azure密钥保管库(Key Vault)来托管存储账号(Storage Account)密钥的示例中,从Github中下载的示例代码在中国区Azure运行时候会遇见各种认证和授权问题, ...

  10. 一. SpringCloud简介与微服务架构

    1. 微服务架构 1.1 微服务架构理解 微服务架构(Microservice Architecture)是一种架构概念,旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦.你可以将其看作是在 ...