UOJ14 DZY Loves Graph 并查集

传送门

题意:给出一张$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 }

转载于:https://www.cnblogs.com/Itst/p/9867583.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值