题目描述

开始有 \(n\) 个点,现在对这 \(n\) 个点进行了 \(m\) 次操作,对于第 \(i\) 个操作(从 \(1\) 开始编号)有可能的三种情况:

  1. \(Add\) a b: 表示在 \(a\) 与$ b$ 之间连了一条长度为 \(i\) 的边(注意, i是操作编号)。保证 \(1≤a,b≤n\)。

  2. \(Delete\) k: 表示删除了当前图中边权最大的\(k\)条边。保证 k 一定不会比当前图中边的条数多。

  3. \(Return\): 表示撤销第 \(i−1\) 次操作。保证第 \(1\) 次操作不是 \(Return\) 且第 \(i−1\)次不是 \(Return\) 操作。

请你在每次操作后告诉\(DZY\)当前图的最小生成树边权和。如果最小生成树不存在则输出 \(0\)。

Input

第一行两个正整数 \(n,m\)。表示有 \(n\) 个点 \(m\) 个操作。

接下来 \(m\) 行每行描述一个操作。

测试点编号 \(n\) \(m\) 其他
1 \(n≤10^3\) \(m≤10^3\) 只有\(Add\)操作
2 \(n≤10^3\) \(m≤10^3\)
3 \(n≤10^3\) \(m≤10^3\)
4 \(n≤2×10^5\) \(m≤2×10^5\) 只有\(Add\)操作
5 \(n≤3×10^5\) \(m≤5×10^5\) 只有\(Add\)操作
6 \(n≤2×10^5\) \(m≤2×10^5\) 没有\(Return\)操作
7 \(n≤3×10^5\) \(m≤5×10^5\) 没有\(Return\)操作
8 \(n≤2×10^5\) \(m≤2×10^5\)
9 \(n≤2×10^5\) \(m≤2×10^5\)
10 \(n≤3×10^5\) \(m≤5×10^5\)

Output

对于每一个操作输出一行一个整数表示当前最小生成树边权和。

Sample Input

2 2
Add 1 2
Return 5 10
Add 2 1
Add 3 2
Add 4 2
Add 5 2
Add 2 3
Return
Delete 1
Add 2 3
Add 5 2
Return

Sample Output

1
0 0
0
0
10
10
10
0
0
15
0

算法一:

\(1-3\)号测试点可以直接每次暴力求最小生成树,时间复杂度\(O(m*n^2α(n))\)得分\(30\)。


算法二:

其次,\(4-5\)号测试点只有\(Add\)操作的测试点可以发现,由于边权是依次递增的

由此,一旦构成最小生成树后,便不会发生改变,直接每次加边按照并查集的方法做。

时间复杂度\(O(nα(n))\),结合算法一,得分\(50\)。


算法三:

我们发现,删边的操作和加边的操作主要是在维护几个连通块

那么,如何做到可撤回的并查集呢?

可持久化并查集,当然是按秩合并的并查集了!!

于是,关于加边和删边的操作我们都解决了。。。

问题主要在\(return\)操作上。

题目中说到,一个\(return\)的操作的前一个操作不可能是\(return\)。

也就是说,若第\(i\)次操作为\(return\)操作,那么,第\(i\)次操作的答案和第\(i-2\)操作答案相同,同时,第\(i-1\)次操作未执行

所以,对于一个\(Add\)或者\(Delete\)操作,我们只用关心下一次操作是不是\(return\)就行了。

于是,我们先进行离线操作。

对于当前操作为\(Add\)或\(Delete\)则看下一次操作,若不为\(return\)直接执行。

否则,执行完操作后存下答案,然后进行撤回操作。

时间复杂度\(O(n*log_n)\),得分\(100\)。


代码如下

#include <bits/stdc++.h>

using namespace std;

#define int long long
#define u64 unsigned long long
#define u32 unsigned int
#define reg register
#define Raed Read
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,x) for(reg int i=Head[x]; i; i=Nxt[i]) inline int Read() {
int res = 0, f = 1;
char c;
while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
do res = (res << 3) + (res << 1) + (c ^ 48);
while (c = getchar(), c >= 48 && c <= 57);
return f ? res : -res;
} template<class T>inline bool Min(T &a, T const&b) {
return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
return a < b ? a = b, 1 : 0;
} const int N=5e5+5; bool MOP1; int n,m; struct node {
int op,a,b;
} Q[N]; struct T3Add {
int Fa[N];
int find(int x) {
return Fa[x]==x?Fa[x]:Fa[x]=find(Fa[x]);
}
inline void solve(void) {
rep(i,1,n)Fa[i]=i;
int tot=0,Ans=0;
rep(i,1,m) {
if(tot==n-1) {
printf("%lld\n",Ans);
continue;
}
int a=find(Q[i].a),b=find(Q[i].b);
if(a!=b) {
tot++,Ans+=i;
Fa[a]=b;
}
if(tot<n-1)puts("0");
else printf("%lld\n",Ans);
}
}
} PAdd; struct T330 {
struct edge {
int a,b,c;
} Edge[N];
int Fa[N];
int find(int x) {
return Fa[x]==x?Fa[x]:Fa[x]=find(Fa[x]);
}
inline void solve(void) {
int tot=0,top=0;
rep(i,1,m) {
int op=Q[i].op;
if(op==1)Edge[++top]=(edge)<%Q[i].a,Q[i].b,i%>;
if(op==2)top-=Q[i].a;
if(op==3) {
if(Q[i-1].op==1)top--;
else top+=Q[i-1].a;
}
int Ans=0,res=0;
rep(j,1,n)Fa[j]=j;
rep(j,1,top) {
int a=find(Edge[j].a),b=find(Edge[j].b);
if(a==b)continue;
res++,Ans+=Edge[j].c,Fa[a]=b;
}
if(res!=n-1)Ans=0;
printf("%lld\n",Ans);
}
}
} P30; struct T3Ac {
int Fa[N],dep[N],Ans[N];
struct edge {
int a,b;
} Edge[N];
int find(int x) {
return Fa[x]==x?Fa[x]:find(Fa[x]);
}
int stack[N];
inline void solve(void) {
int cnt=0,tot=0,top=0;
rep(i,1,n)Fa[i]=i;
rep(i,1,m) {
int op=Q[i].op;
if(op==1) {
int a=find(Q[i].a),b=find(Q[i].b);
if(a!=b)cnt++,tot+=i;
if(cnt==n-1)Ans[i]=tot;
else Ans[i]=Ans[i-1];
if(Q[i+1].op==3) {
Ans[i+1]=Ans[i-1];
if(a!=b)cnt--,tot-=i;
} else {
stack[++top]=i;
if(a!=b) {
if(dep[a]>dep[b])swap(a,b);
Fa[a]=b,Max(dep[b],dep[a]+1);
Edge[i].a=a,Edge[i].b=b;
}
}
}
if(op==2) {
Ans[i]=Ans[stack[top-Q[i].a]];
if(Q[i+1].op==3)Ans[i+1]=Ans[i-1];
else {
int k=Q[i].a;
rep(j,1,k) {
int Id=stack[top--],A=Edge[Id].a;
if(!A)continue;
Fa[A]=A;
cnt--,tot-=Id;
}
}
}
printf("%lld\n",Ans[i]);
}
}
} P100; char S[15]; bool MOP2; inline void _main(void) {
n=Raed(),m=Read();
int f=0;
rep(i,1,m) {
scanf("%s",S);
if(S[0]!='A')f=1;
if(S[0]=='A')Q[i]=(node)<%1,Read(),Read()%>;
if(S[0]=='D')Q[i]=(node)<%2,Read(),0%>;
if(S[0]=='R')Q[i]=(node)<%3,0,0%>;
}
if(!f)PAdd.solve();
else if(n<=1000&&m<=1000)P30.solve();
else P100.solve();
} signed main() {
#define offline1
#ifdef offline
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
_main();
fclose(stdin);
fclose(stdout);
#else
_main();
#endif
return 0;
}

[UER #1] DZY Loves Graph的更多相关文章

  1. UOJ14 UER #1 DZY Loves Graph(最小生成树+并查集)

    显然可以用可持久化并查集实现.考虑更简单的做法.如果没有撤销操作,用带撤销并查集暴力模拟即可,复杂度显然可以均摊.加上撤销操作,删除操作的复杂度不再能均摊,但注意到我们在删除时就可以知道他会不会被撤销 ...

  2. 【UER #1】[UOJ#12]猜数 [UOJ#13]跳蚤OS [UOJ#14]DZY Loves Graph

    [UOJ#12][UER #1]猜数 试题描述 这一天,小Y.小D.小C正在愉快地玩耍. 小Y是个数学家,他一拍脑袋冒出了一个神奇的完全平方数 n. 小D是个机灵鬼,很快从小Y嘴里套出了 n的值.然后 ...

  3. 学长小清新题表之UOJ 14.DZY Loves Graph

    学长小清新题表之UOJ 14.DZY Loves Graph 题目描述 \(DZY\)开始有 \(n\) 个点,现在他对这 \(n\) 个点进行了 \(m\) 次操作,对于第 \(i\) 个操作(从 ...

  4. 【UER #1】DZY Loves Graph

    UOJ小清新题表 题目内容 UOJ链接 DZY开始有\(n\)个点,现在他对这\(n\)个点进行了\(m\)次操作,对于第\(i\)个操作(从\(1\)开始编号)有可能的三种情况: Add a b: ...

  5. 【UOJ #14】【UER #1】DZY Loves Graph

    http://uoj.ac/problem/14 题解很好的~ 不带路径压缩的并查集能保留树的原本形态. 按秩合并并查集可以不用路径压缩,但是因为此题要删除,如果把深度当为秩的话不好更新秩的值,所以把 ...

  6. 【UER #1】DZY Loves Graph(待卡常数)

    题解: 正解是可持久化并查集 但这个显然是lct可以维护的 但这常数是个问题啊??? #include <bits/stdc++.h> using namespace std; struc ...

  7. uoj #14.【UER #1】DZY Loves Graph

    http://uoj.ac/problem/14 由于加入的边权递增,可以直接运行kruskal并支持撤销,但这样如果反复批量删边和撤销,时间复杂度会退化,因此需要对删边操作加上延时处理,只有在删边后 ...

  8. 2019.01.22 uoj#14. 【UER #1】DZY Loves Graph(并查集)

    传送门 题意简述: 要求支持以下操作: 在a与b之间连一条长度为i的边(i是操作编号):删除当前图中边权最大的k条边:表示撤销第 i−1次操作,保证第1次,第i−1 次不是撤回操作. 要求在每次操作后 ...

  9. UOJ14 DZY Loves Graph

    DZY开始有 nn 个点,现在他对这 nn 个点进行了 mm 次操作,对于第 ii 个操作(从 11 开始编号)有可能的三种情况: Add a b: 表示在 aa 与 bb 之间连了一条长度为 ii ...

随机推荐

  1. Linux开机启动和登录时各个文件的执行顺序

    1.在Linux内核被加载后,第一个运行的程序便是/sbin/init 该文件会读取/etc/inittab文件,并依据此文件来进行初始化工作.其中/etc/inittab文件最主要的作用就是设定Li ...

  2. Redis数据结构常用命令

    Redis数据结构--字符串

  3. 一、mybatis的插件介绍

    摘自:https://www.cnblogs.com/qm-article/p/11785350.html mybatis的插件机制   一.mybatis的插件介绍 关于mybatis的插件,我想大 ...

  4. Spring Boot使用阿里云证书启用HTTPS

    1.到阿里云下载证书页面下载证书 2.根据页面内容,可以使用2种证书:PFX JKS 把对应证书放到src/main/resources目录下 在application.properties文件中加入 ...

  5. [论文理解] Receptive Field Block Net for Accurate and Fast Object Detection

    Receptive Field Block Net for Accurate and Fast Object Detection 简介 本文在SSD基础上提出了RFB Module,利用神经科学的先验 ...

  6. P1533可怜的狗狗

    困死了,完全做不下去题 就当是对莫队最最基本的思想的一个复习叭(只有最最基本的思想,没有莫队) 传送 我们可以很容易的想到这题要用线段树. 60pts 此题要求某个区间里第K小的数,可以暴力的考虑对每 ...

  7. 【C++】C++类的static 关键字理解

    转载自:ZJE_ANDY static修饰类中成员,表示类的共享数据 1.static类成员 C++primer里面说过,static类成员不像普通的类数据成员,static类数据成员独立于一切类对象 ...

  8. react native props上存在的属性,显示不存在

    问题:类型“Readonly<{}> & Readonly<{ children?: ReactNode; }>”上不存在属性“navigation”.ts(2339) ...

  9. 从txt导入数据到mysql

    当要往mysql的table中录入数据量大的时候,直接从txt录入已有数据是一个愉快的选择. 在录入数据前要做一些格式上的准备 1. txt编码要是utf-8,无BOM 2. 每行以\t\n结尾,每列 ...

  10. sklearn.feature_extraction.DictVectorizer

    sklearn.feature_extraction.DictVectorizer:将字典组成的列表转换成向量.(将特征与值的映射字典组成的列表转换成向量) 1. 特征矩阵行代表数据,列代表特征,0表 ...