Codeforces 1416 D. Graph and Queries —— 启发式合并

This way

题意:

现在有一张图,每次有两种询问:
1 x 表示在x所在的连通块中,找到值最大的数输出,并且将那个值变成0
2 i 表示将第i条边删掉

题解:

将值变成0这个操作限制了我们一定要从前往后做。
但是我偏不,因为从前往后很难找到当前的连通块是什么,但是我们可以从后往前,对于当前如果是2操作,并且如果x和y不在一个集合内,就将其连起来。注意因为我们之后是要从前往后做的,所以不能路径压缩,需要使用启发式合并,将两个集合的数合并在一起。然后从前往后做的时候,再将大集合中删掉小集合的数。
大致思路就是这样,代码不是我敲的所以…就不解释了

#include<bits/stdc++.h>
#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=(a);i>=(b);i--)
#define perr(i,a,b) for(int i=(a);i>(b);i--)
#define pb push_back
#define eb emplace_back
#define mst(a,b) memset(a,b,sizeof(a))
#define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin))
#pragma GCC optimize(2)
#define ri register int
#define iv inline void
using namespace std;
const int N=3e5+5;
int f[N],ch[N][2],rev[N],siz[N];
const int SZ=1<<19;
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int in(){
    G;while(*ip<'-')G;
    ri x=*ip&15;G;
    while(*ip>'-'){x*=10;x+=*ip&15;G;}
    return x;
}
using namespace std;
//using namespace __gnu_pbds;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const ll LINF=0x3f3f3f3f3f3f3f3f;
const double eps=1e-12;
const double PI=acos(-1.0);
const double angcst=PI/180.0;
const ll mod=998244353;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll qmul(ll a,ll b){ll r=0;while(b){if(b&1)r=(r+a)%mod;b>>=1;a=(a+a)%mod;}return r;}
ll qpow(ll a,ll n){ll r=1;while(n){if(n&1)r=(r*a)%mod;n>>=1;a=(a*a)%mod;}return r;}
ll qpow(ll a,ll n,ll p){ll r=1;while(n){if(n&1)r=(r*a)%p;n>>=1;a=(a*a)%p;}return r;}

int n,m,q;

int orid[1000050];

int v[1000050];
set<int> st[1000050];
vector<int> vec[1000050];
vector<int> mainvec[1000050];

int a[1000050],b[1000050];
int opr[1000050],opid[1000050];

bool vis[1000050];

int gp[1000050];
/*int fnd(int p)
{
    return p==gp[p]?p:(gp[p]=fnd(gp[p]));
}*/

void mergeTo(vector<int> &v,int a,int b)
{
    a=gp[a];
    b=gp[b];
    if(a^b)
    {
        if(vec[a].size()>vec[b].size())
            swap(a,b);
        v=vec[a];
        for(int &it:vec[a])
        {
            gp[it]=b;
            vec[b].pb(it);
        }
        vec[a].clear();
    }
}

void solve()
{
    scanf("%d%d%d",&n,&m,&q);
    rep(i,1,n)
    {
        scanf("%d",&v[i]);
        orid[v[i]]=i;
        gp[i]=i;
        vec[i].pb(i);
    }
    rep(i,1,m)
        scanf("%d%d",&a[i],&b[i]);
    rep(i,1,q)
    {
        scanf("%d%d",&opr[i],&opid[i]);
        if(opr[i]==2)
            vis[opid[i]]=true;
    }
    
    rep(i,1,m)
    {
        if(!vis[i])
            mergeTo(mainvec[0],a[i],b[i]);
    }
    
    per(i,q,1)
    {
        if(opr[i]==2)
            mergeTo(mainvec[i],a[opid[i]],b[opid[i]]);
    }
    
    rep(i,1,n)
    {
        if(gp[i]==i)
        {
            for(int &it:vec[i])
                st[i].insert(v[it]);
        }
    }
    
    int gpid=n;
    rep(i,1,q)
    {
        if(opr[i]==1)
        {
            bool flag=false;
            int fa=gp[opid[i]];
            //cerr<<"? "<<i<<' '<<fa<<' '<<st[fa].size()<<'\n';
            while(!st[fa].empty())
            {
                auto it=st[fa].rbegin();
                //cerr<<"! "<<fa<<' '<<st[fa].size()<<' '<<(*it)<<'\n';
                if(gp[orid[*it]]==fa)
                {
                    v[orid[*it]]=0;
                    flag=true;
                    cout<<(*it)<<'\n';
                    st[fa].erase(*it);
                    break;
                }
                st[fa].erase(*it);
            }
            if(!flag)
                cout<<"0\n";
        }
        else
        {
            ++gpid;
            for(int &it:mainvec[i])
            {
                gp[it]=gpid;
                if(v[it])
                    st[gpid].insert(v[it]);
            }
        }
    }
}
int main()
{
    closeSync;
    //multiCase
    {
        solve();
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值