题意:给出一张$N$个点,最开始没有边的图,$M$次操作,操作为加入边(边权为当前的操作编号)、删除前$K$大边、撤销前一次操作,每一次操作后询问最小生成树边权和。$N \leq 3 \times 10^5 , M \leq 5 \times 10^5$
可以发现可以直接大力用并查集做,因为一条边只要合并了两个集合就能产生贡献。
关于删除可以将边的加入扔到栈里面,删除的时候不断弹栈即可。
撤销操作对于加边就是删掉了一条边,而对于删边就相当于什么都不做,直接做即可。
加入每一条边之后的答案要一起放在栈里面,这样删边+撤销的答案询问就可以$O(1)$解决。
#include<bits/stdc++.h> using namespace std; inline int read(){ ; ; char c = getchar(); while(!isdigit(c)){ if(c == '-') f = ; c = getchar(); } while(isdigit(c)){ a = (a << ) + (a << ) + (c ^ '); c = getchar(); } return f ? -a : a; } , MAXM = ; int fa[MAXN] , size[MAXN] , top , N , M , lasStep; ]; bool isadd; ]; inline int find(int x){ while(fa[x] != x) x = fa[x]; return x; } inline void init(){ ; i <= N ; i++){ fa[i] = i; size[i] = ; } } inline void merge(int a , int b , int k){ a = find(a); b = find(b); if(a != b){ if(size[a] > size[b]) swap(a , b); fa[a] = b; size[b] += size[a]; s[++top][] = a; s[top][] = b; s[top][] = s[top - ][] + k; s[top][] = s[top - ][] + ; } else{ s[top][] = s[++top][] = ; s[top][] = s[top - ][]; s[top][] = s[top - ][]; } } inline void pop(int k){ while(k--){ ]){ size[s[top][]] -= size[s[top][]]; fa[s[top][]] = s[top][]; } top--; } } int main(){ N = read(); M = read(); init(); scanf("%s",ss); ; i <= M ; i++){ ] == 'A'){ merge(read() , read() , i); cout << (s[top][] == N - ? s[top][] : 0ll) << '\n'; if(i == M) break; scanf("%s",ss); ] == 'R') pop(); } else ] == 'D'){ lasStep = read(); cout << (s[top - lasStep][] == N - ? s[top - lasStep][] : 0ll) << '\n'; if(i == M) break; scanf("%s",ss); ] != 'R') pop(lasStep); } else{ cout << (s[top][] == N - ? s[top][] : 0ll) << '\n'; if(i == M) break; scanf("%s",ss); } } ; }