题意:给出一张$N$个点,最开始没有边的图,$M$次操作,操作为加入边(边权为当前的操作编号)、删除前$K$大边、撤销前一次操作,每一次操作后询问最小生成树边权和。$N \leq 3 \times 10^5 , M \leq 5 \times 10^5$
可以发现可以直接大力用并查集做,因为一条边只要合并了两个集合就能产生贡献。
关于删除可以将边的加入扔到栈里面,删除的时候不断弹栈即可。
撤销操作对于加边就是删掉了一条边,而对于删边就相当于什么都不做,直接做即可。
加入每一条边之后的答案要一起放在栈里面,这样删边+撤销的答案询问就可以$O(1)$解决。
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 inline int read(){ 5 int a = 0; 6 bool f = 0; 7 char c = getchar(); 8 while(!isdigit(c)){ 9 if(c == '-') 10 f = 1; 11 c = getchar(); 12 } 13 while(isdigit(c)){ 14 a = (a << 3) + (a << 1) + (c ^ '0'); 15 c = getchar(); 16 } 17 return f ? -a : a; 18 } 19 20 const int MAXN = 300010 , MAXM = 500010; 21 int fa[MAXN] , size[MAXN] , top , N , M , lasStep; 22 long long s[MAXM][4]; 23 bool isadd; 24 char ss[10]; 25 26 inline int find(int x){ 27 while(fa[x] != x) 28 x = fa[x]; 29 return x; 30 } 31 32 inline void init(){ 33 for(int i = 1 ; i <= N ; i++){ 34 fa[i] = i; 35 size[i] = 1; 36 } 37 } 38 39 inline void merge(int a , int b , int k){ 40 a = find(a); 41 b = find(b); 42 if(a != b){ 43 if(size[a] > size[b]) 44 swap(a , b); 45 fa[a] = b; 46 size[b] += size[a]; 47 s[++top][0] = a; 48 s[top][1] = b; 49 s[top][2] = s[top - 1][2] + k; 50 s[top][3] = s[top - 1][3] + 1; 51 } 52 else{ 53 s[top][0] = s[++top][1] = 0; 54 s[top][2] = s[top - 1][2]; 55 s[top][3] = s[top - 1][3]; 56 } 57 } 58 59 inline void pop(int k){ 60 while(k--){ 61 if(s[top][0]){ 62 size[s[top][1]] -= size[s[top][0]]; 63 fa[s[top][0]] = s[top][0]; 64 } 65 top--; 66 } 67 } 68 69 int main(){ 70 N = read(); 71 M = read(); 72 init(); 73 scanf("%s",ss); 74 for(int i = 1 ; i <= M ; i++){ 75 if(ss[0] == 'A'){ 76 merge(read() , read() , i); 77 cout << (s[top][3] == N - 1 ? s[top][2] : 0ll) << '\n'; 78 if(i == M) 79 break; 80 scanf("%s",ss); 81 if(ss[0] == 'R') 82 pop(1); 83 } 84 else 85 if(ss[0] == 'D'){ 86 lasStep = read(); 87 cout << (s[top - lasStep][3] == N - 1 ? s[top - lasStep][2] : 0ll) << '\n'; 88 if(i == M) 89 break; 90 scanf("%s",ss); 91 if(ss[0] != 'R') 92 pop(lasStep); 93 } 94 else{ 95 cout << (s[top][3] == N - 1 ? s[top][2] : 0ll) << '\n'; 96 if(i == M) 97 break; 98 scanf("%s",ss); 99 } 100 } 101 return 0; 102 }