UOJ14 DZY Loves Graph

UOJ14 DZY Loves Graph

我的解法:启发式并查集+状态建树

观察题目的三种操作

​ 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 操作。

发现:

​ Add操作均是从上一个状态转移加边;

​ Delete相当于将状态转移到k条边之前

​ Return则是上上次状态

发现这个状态其实是一棵树

我直接将这个树建了出来

对于每个点来说,其实它加边的情况就是根节点到它的路径

所以dfs时入栈加边,出栈删边即可

接下来我们讨论加、删边操作---->

首先,这道题边权从小到大给出,所以可以直接类似Kruskal地加边,并查集维护(极其显然)

并查集加边想必没有问题,那如何维护删边呢?

我用(向)了(大佬)一(学习)种(了)启发式并查集(按秩合并),不使用路径压缩来维护

我们通常并查集压缩路径后是O(1)复杂度,但是如果放弃路径合并,每次更新就只有一次更改

放弃路径合并的话,如何保证复杂度?

记录当前并查集子树的最大深度,每次将深度小的集合接上去

这样最坏情况下是深度是log(n),保证了查询复杂度

至此,问题就解决了

#include<bits/stdc++.h>
using namespace std;
 
#define reg register
typedef long long ll;
#define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i)
 
 
char IO;
inline int rd(){
    int s=0;
    while(!isdigit(IO=getchar()));
    do s=(s<<1)+(s<<3)+(IO^'0');
    while(isdigit(IO=getchar()));
    return s;
}
 
const int N=3e5+10,M=5e5+10;
 
struct Edge{
    int to,nxt;
}e[M];
int head[M],ecnt;
inline void AddEdge(int u,int v){
    e[++ecnt]=(Edge){v,head[u]};
    head[u]=ecnt;
}
#define erep(u,i) for(int i=head[u];i;i=e[i].nxt)
 
int n,m,cnt;
ll ans;
 
int opt[M],a[M],b[M];
int q[M],r,now[M];
int f[M];
int d[M],t[M];
int dep[N],fa[N];
inline int Find(int x){ 
    while(x!=fa[x]) x=fa[x];
    return x; 
}
void merge(int x,int y,int w){
    x=Find(x),y=Find(y);
    if(x==y) return;
    if(dep[x]>dep[y]) swap(x,y);
    fa[x]=y;
    d[w]=y,t[w]=dep[y],f[w]=x;//记录更改的值
    dep[y]=(dep[y]<dep[x]+1)?dep[x]+1:dep[y];
    cnt--,ans+=w;
}
ll res[M];
 
void dfs(int u){
    merge(a[u],b[u],u);//加边
    if(cnt<=1) res[u]=ans;
    else res[u]=0;
    erep(u,i) {
        int v=e[i].to;
        dfs(v);
    }
    if(f[u]) ans-=u,cnt++,dep[d[u]]=t[u],fa[f[u]]=f[u];//删除
}
inline void wt(ll x){
    int buf[21],l=0;
    while(x) buf[++l]=x%10,x/=10;
    drep(i,l,1) putchar('0'^buf[i]);
    putchar('\n');
}
 
int main(){
    n=rd(),m=rd();
    rep(i,1,n) fa[i]=i; cnt=n;
    rep(i,1,m) {
        char s[10];
        scanf("%s",s);
        if(s[0]=='A') opt[i]=1,a[i]=rd(),b[i]=rd();
        else if(s[0]=='D') opt[i]=2,a[i]=rd();
        else opt[i]=3;
    }
    rep(i,1,m) {
        if(opt[i]==1){
            AddEdge(now[i-1],i);
            now[i]=q[++r]=i;//维护状态树
        } else if(opt[i]==2) now[i]=q[(r-=a[i])];
        else {
            if(opt[i-1]==1) r--;
            else r+=a[i-1];
            now[i]=now[i-2];
        }
    }
    dfs(1);
    rep(i,1,m){
        if(opt[i]^1) res[i]=res[now[i]];
        if(res[i]) wt(res[i]);
        else puts("0");
    }
}
​```
posted @ 2019-08-06 13:46  chasedeath  阅读(175)  评论(0编辑  收藏  举报