云智第四节学习记录

· · 个人记录

第四节-并查集及其技巧(种类并查集,带权并查集,可撤销并查集,扩展域并查集,启发式合并,按秩合并)

A 题:P3367 【模板】并查集

板题,切掉了。

清朝代码如下:

#include<bits/stdc++.h>
using namespace std;
int sets[1000000];
int find(int x){
    if(sets[x]!=x)sets[x]=find(sets[x]);
    return sets[x];
}
void unoin(int &x,int &y){
    x=find(x),y=find(y);
    sets[x]=y;
}
bool nouoi(int x,int y){
    if(find(x)==find(y))return true;
    else return false;
}
int main() {
    int q,m;
    cin>>q>>m;
    for(int i=1;i<=q;i++)sets[i]=i;
    for(int i=0;i<m;i++){
        int x,y,z;
        cin>>x>>y>>z;
        if(x==1)unoin(y,z);
        else {
            if(nouoi(y,z))cout<<"Y\n";
            else cout<<"N\n";
        }
    }
    return 0;
}

B 题:P1955 [NOI2015] 程序自动分析

一点思维罢了。

清朝代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn=3E6+5;
const int mod=2999999;
int t;
int n,cnt;
struct st {
    int x,y,k;
} a[maxn];
vector<pair<int,int> > mp[maxn];

void Insert(int x) {
    for(int i=0; i<mp[x%mod].size(); i++) {
        if(mp[x%mod][i].first==x)return ;
    }
    mp[x%mod].push_back(make_pair(x,++cnt));
}
int Hash(int x) {
    for(int i=0; i<mp[x%mod].size(); i++) {
        if(mp[x%mod][i].first==x) {
            return mp[x%mod][i].second;
        }
    }
}
int f[maxn];
int Find(int x) {
    if(x==f[x])return x;
    return f[x]=Find(f[x]);
}
void Merge(int x,int y) {
    x=Find(x);
    y=Find(y);
    if(x==y)return ;
    f[x]=y;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    cin>>t;
    while(t--) {
        bool flag=0;
        for(int i=0; i<mod; i++)mp[i].clear();
        cnt=0;

        cin>>n;
        for(int i=1; i<=n; i++) {
            cin>>a[i].x>>a[i].y>>a[i].k;
            Insert(a[i].x);
            Insert(a[i].y);
        }
        for(int i=1; i<=cnt; i++)f[i]=i;
        for(int i=1; i<=n; i++) {
            int x,y,k;
            x=Hash(a[i].x);
            y=Hash(a[i].y);
            k=a[i].k;
            if(k==1) {
                Merge(x,y);
            }
        }
        for(int i=1; i<=n; i++) {
            int x,y,k;
            x=Hash(a[i].x);
            y=Hash(a[i].y);
            k=a[i].k;
            if(k==0) {
                if(Find(x)==Find(y)) {
                    flag=1;
                    break;
                }
            }
        }
        if(flag==1) {
            cout<<"NO\n";
        } else {
            cout<<"YES\n";
        }
    }
    return 0;
}

C 题:P1197 [JSOI2008] 星球大战

运用离线算法倒序维护并查集,这题我写过类似的,像关闭农场之类的。
本来可以切掉的,却被卡到凌晨一点,结果无赖对拍时发现是自己范围开小了,难崩。

现代代码如下

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define I_love_Foccarus return
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define deap(i,a,b) for(int i=a;i>=b;i--)
#define in(a) a=read()
const int N = 2e6+5;
const int inf = INT_MAX;
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9') {
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*f;
}
void fast() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
struct Edge {
    int act,nex;
} edge[N<<2];
int head[N],eid;
void eadd(int u,int v) {
    eid++;
    edge[eid].act=v,edge[eid].nex=head[u];
    head[u]=eid;
}
int fa[N],t[N],st[N],out[N],otop;
int find(int u) {
    return fa[u]==u?u:fa[u]=find(fa[u]);
}
int ans;
void dfs(int u) {
    for(int i=head[u]; i; i=edge[i].nex) {
        int v=edge[i].act;
        if(!t[v]&&find(u)!=find(v)) {
            fa[find(u)]=find(v);
            ans--;
            dfs(v);
        }
    }
}      
int k; 
int n,m;
signed main() {
    //fast();
    in(n),in(m); 
    n--;  
    rep(i,0,n)fa[i]=i;
    rep(i,1,m) {
        int x,y;
        in(x),in(y);
        eadd(x,y),eadd(y,x);
    }
    in(k);
    rep(i,1,k) {
        in(st[i]);
        t[st[i]]=1;
    }

    ans=n-k+1;
    rep(i,0,n) {
        if(!t[i])dfs(i);
    }
    out[++otop]=ans;
    deap(j,k,1) {
        ans++,t[st[j]]=0;
        for(int i=head[st[j]]; i; i=edge[i].nex) {
            int v=edge[i].act;
            if(!t[v]&&find(st[j])!=find(v)) {
                fa[find(st[j])]=find(v);
                ans--;
            }
        }
        head[st[j]]=0; 
        out[++otop]=ans;
    }
    while(otop) {
        printf("%d\n",out[otop--]);
    }
    return 0;
}

D 题:P2024 [NOI2001] 食物链

种类并查集板题,切了。

现代代码如下

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define deap(i,a,b) for(int i=a;i>=b;i--)
#define in(a) a=read()
const int N = 2e6+5;
const int inf = INT_MAX;
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9') {
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*f;
}
void fast() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
int fa[N];
int find(int u) {
    return fa[u]==u?u:fa[u]=find(fa[u]);
}

signed main() {
    //fast();
    int n,k,ans=0;
    in(n),in(k);
    rep(i,0,n*3) fa[i]=i;
    rep(i,1,k) {
        int op,x,y;
        in(op),in(x),in(y);
        if (x>n||y>n) {
            ans++;
        } else if(op==1) {
            if(find(n+x)==find(y)||find(n+y)==find(x))  {
                ans++;
            }  else {
                fa[find(x)]=find(y);
                fa[find(n+x)]=find(n+y);
                fa[find((n<<1)+x)]=find((n<<1)+y);
            }
        } else {
            if (find(x)==find(y)||find(x)==find(n+y)) {
                ans++;
            } else {
                fa[find(n+x)]=find(y);
                fa[find((n<<1)+x)]=find(n+y);
                fa[find(x)]=find((n<<1)+y);
            }
        }
    }
    cout<<ans;
    return 0;
}

E 题:P1196 [NOI2002] 银河英雄传说

带权并查集板题,切了。

清朝代码如下

#include<bits/stdc++.h>
using namespace std;
int s[30005];
int d[30005];
int zs[30005];
int find(int i) {
    if(s[i]!=i) {
        int root=find(s[i]);
        d[i]+=d[s[i]];
        s[i]=root;
        return root;
    }
    return s[i];
}
void merge(int x,int y) {
    int fa=find(x),fb=find(y);
    d[fa]+=zs[fb];
    s[fa]=fb;
    zs[fb]+=zs[fa];
    zs[fa]=0;
}
bool access(int x,int y){
    if(find(x)==find(y))return true;
    else return false;
}
int inquire(int x,int y) {
    if(!access(x,y))return -1;
    else return abs(d[x]-d[y])-1;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int n=30000,m;
    cin>>m;
    for(int i=1; i<=n; i++)s[i]=i,d[i]=0,zs[i]=1;
    for(int i=0; i<m; i++) {
        char x;
        cin>>x;
        if(x=='M') {
            int a,b;
            cin>>a>>b;
            merge(a,b);
        } else {
            int a,b;
            cin>>a>>b;
            cout<<inquire(a,b)<<'\n';
        }
    }
    return 0;
}

F 题:Destroying Array

采用离线算法,从后往前回复被删除的点,最大连续和可以用带权并查集维护。

现代代码如下

#include<iostream>
#include<cstring>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define deap(i,a,b) for(int i=a;i>=b;i--)
#define in(a) a=read()
const int N = 1e5+5;
const int inf = INT_MAX;
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9') {
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*f;
}
void fast() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
int fa[N];
long long ans[N],t[N];
int find(int u) {
    return fa[u]==u?u:fa[u]=find(fa[u]);
}
signed main() {
    //fast();
    int n;
    in(n);
    int *a=new int[n+1],*b=new int[n+1];
    rep(i,1,n)in(a[i]);
    rep(i,1,n)in(b[i]);
    rep(i,1,n)fa[i]=i;
    ans[n]=0;
    memset(t,-1,sizeof(t));
    deap(i,n-1,1) {
        t[b[i+1]]=a[b[i+1]];
        if(b[i+1]-1>0&&~t[b[i+1]-1]) {
            t[b[i+1]]+=t[find(b[i+1]-1)];
            fa[find(b[i+1]-1)]=find(b[i+1]);
        }
        if(b[i+1]+1<=n&&~t[b[i+1]+1]) {
            t[b[i+1]]+=t[find(b[i+1]+1)];
            fa[find(b[i+1]+1)]=find(b[i+1]);
        }
        ans[i]=max(ans[i+1],t[b[i+1]]);
    }
    rep(i,1,n)printf("%lld\n",ans[i]);
    return 0;
}

G 题:P3402 可持久化并查集

可持久化并查集板题,某人因为此题再度熬到凌晨一点。可持久化并查集可以通过可持久化数组实现,这也就要使用我们伟大的主席树算法了。
我快速的写完了代码,交了上去,结果——常数被卡了。
考虑优化常数。按秩合并?摆烂,直接随机化吧。
舍伍德算法启动。——依旧被卡。
启发式合并——被卡依旧。
错误的按深度合并——死循环。
现在是北京时间一点一十二分~
\bx 什么意思啊?GTP 改错……
越改越错……
受不了了!睡觉!
刷牙中……
等等,我的一个下标貌似多减了一个1?!!
还真是,AC——qnq。

代码如下:

#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define I_love_Foccarus return
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define deap(i,a,b) for(int i=a;i>=b;i--)
#define in(a) a=read()
const int N = 1e6+5;
const int inf = INT_MAX;
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9') {
        if (ch=='-') f=-1;
        ch=getchar();
    }
    while (ch>='0'&&ch<='9') {
        x=x*10+ch-48;
        ch=getchar();
    }
    return x*f;
}
void fast() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}
int tot;
struct Tree {
    int l,r,v,sz;
};
Tree t[N<<5];
inline void push_back(Tree x) {
    t[++tot]=x;
}
int root[N];
int n,m,a[N],b[N];
int js;
inline int change(int idx,int l,int r,int q,int be) {
    Tree tmp=t[idx];
    if(l==r) {
        tmp.v=be;
        push_back(tmp);
        return tot;
    }
    int mid=(l+r)>>1;
    if(q<=mid) {
        tmp.l=change(tmp.l,l,mid,q,be);
    } else {
        tmp.r=change(tmp.r,mid+1,r,q,be);
    }
    push_back(tmp);
    return tot;
}
inline int add(int idx,int l,int r,int q) {
    Tree tmp=t[idx];
    if(l==r) {
        tmp.sz++;
        push_back(tmp);
        return tot;
    }
    int mid=(l+r)>>1;
    if(q<=mid) {
        tmp.l=add(tmp.l,l,mid,q);
    } else {
        tmp.r=add(tmp.r,mid+1,r,q);
    }
    push_back(tmp);
    return tot;
}
inline int query(int idx,int l,int r,int q) {
    if(l==r) {
        return idx;
    }
    int mid=(l+r)>>1;
    if(q<=mid)return query(t[idx].l,l,mid,q);
    else return query(t[idx].r,mid+1,r,q);
}
inline int build(int l,int r) {
    Tree tmp;
    if(l==r) {
        tmp.l=tmp.r=-1,tmp.v=l,tmp.sz=0;
        push_back(tmp);
        return tot;
    }
    int mid=(l+r)>>1;
    tmp.l=build(l,mid),tmp.r=build(mid+1,r);
    tmp.v=tmp.sz=-1;
    push_back(tmp);
    return tot;
}
int anser;
inline int find(int qt,int u) {
    int v=query(root[qt],1,n,u);
    if(u==t[v].v)return v;
    else return find(qt,t[v].v);

}
signed main() {
    //fast();
    in(n),in(m);
    root[0]=build(1,n);
    rep(i,1,m) {
        int op,qx,qy,val,l,r;
        in(op),in(qx);
        if(op==1) {
            root[i]=root[i-1];
            in(qy);
            int fx=find(i,qx),fy=find(i,qy);
            if(t[fx].v!=t[fy].v) {
                if(t[fx].sz>t[fy].sz)swap(fx,fy);
                root[i]=change(root[i-1],1,n,t[fx].v,t[fy].v);
                if(t[fx].sz==t[fy].sz)
                root[i]=add(root[i],1,n,t[fy].v);
            } 
        } else if(op==2) {
            root[i]=root[qx];
        } else {
            in(qy);            
            cout<<(find(i-1,qx)==find(i-1,qy))<<'\n';
            root[i]=root[i-1];
        }

    }
  return 0;
}