zoukankan      html  css  js  c++  java
  • 【loj 3275】「JOISC 2020 Day2」有趣的 Joitter 交友【启发式合并】

    传送门

    Solution

    首先考虑转化题意,以下用(scc)指代强连通分量

    活动的每一步相同于:如果(y,z)在同一个(scc)中,(x)(y)有连边,那么(x)就可以向(z)连边。

    也就是说,对于一个(scc),如果(x)(scc)中连了一条边,那么(x)就可以在活动后向(scc)中的任何一个点连边。

    那么分别考虑每个(scc)的贡献就是内部的贡献(siz(siz-1)+)(scc)连了边的点的贡献(sizcdot x),其中(x)时向该(scc)连边的点的数量。

    于是,对于每一个(scc)开一个(set) (pt[i])维护内部的点,一个(set) (in[i])维护向该(scc)连边的点。

    对于加入的每一条边((x,y))

    (1.)((x,y))在同一个(scc)中:直接跳过

    (2.)如果(y)所在(scc)本来就向(x)所在(scc)连了边,那么需要合并这两个(scc)

    (3.)否则,直接在(in[y])中加入(x)

    为了判断是否可以合并(scc),还需要额外维护两个(set)(ins[s])(outs[s])表示向(s)连边的(scc)以及(s)连出去的(scc),合并时使用启发式合并,依次考虑每一个(set)即可

    注意到合并后可能还会导致新的需要合并的(scc),于是开一个·队列先后合并即可。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e5+10;
    typedef long long ll;
    int n,m,fa[N];
    inline int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    set<int> pt[N];//点
    set<int> ins[N];//连入的强连通分量
    set<int> outs[N];//连出的强连通分量 
    set<int> in[N];//连入的点 
    inline ll calc(int x){
    	return 1ll*pt[x].size()*(pt[x].size()-1+in[x].size());
    }
    typedef pair<int,int> pii;
    #define mp make_pair 
    #define it set<int>::iterator
    ll ans=0,ret=0;
    queue<pii>q;
    inline void merge(int x,int y){
    	int fx=find(x),fy=find(y);
    	if(fx==fy) return ;
    	ans-=calc(fx)+calc(fy);
    	if(pt[fx].size()>pt[fy].size()) swap(fx,fy),swap(x,y);
    	fa[fx]=fy;
    	if(ins[fx].count(fy)) ins[fx].erase(fy),outs[fy].erase(fx);
    	if(outs[fx].count(fy)) outs[fx].erase(fy),ins[fy].erase(fx);
    	for(it i=pt[fx].begin();i!=pt[fx].end();i++){
    		int s=*i;
    		if(in[fy].count(s)) in[fy].erase(s);
    		pt[fy].insert(*i);
    	}
    	for(it i=ins[fx].begin();i!=ins[fx].end();i++){
    		int s=*i;
    		if(outs[fy].count(s)) q.push(mp(s,fy));
    		else ins[fy].insert(s);outs[s].erase(fx);outs[s].insert(fy);
    	} 
    	for(it i=outs[fx].begin();i!=outs[fx].end();i++){
    		int s=*i;
    		if(ins[fy].count(s)) q.push(mp(s,fy));
    		else outs[fy].insert(s);ins[s].erase(fx);ins[s].insert(fy);
    	}
    	for(it i=in[fx].begin();i!=in[fx].end();i++)
    		if(!pt[fy].count(*i)) in[fy].insert(*i);
    	ans+=calc(fy);
    }
    inline void work(int x,int y){
    	q.push(mp(x,y));
    	while(!q.empty())
    		merge(q.front().first,q.front().second),q.pop();
    }
    int main(){	
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;++i) fa[i]=i,pt[i].insert(i);
    	for(int i=1;i<=n;++i) ans+=calc(i);
    	for(int i=1,a,b;i<=m;++i){
    		scanf("%d%d",&a,&b);
    		int fx=find(a),fy=find(b);
    		if(fx==fy){
    			printf("%lld
    ",ans);
    			continue;
    		}
    		if(ins[fx].count(fy)) work(a,b);
    		else{
    			ans-=calc(fy);
    			ins[fy].insert(fx);	outs[fx].insert(fy);
    			in[fy].insert(a);
    			ans+=calc(fy);
    		}
    		printf("%lld
    ",ans);
    	} 
    	return 0;
    }
    
  • 相关阅读:
    90.子类调用父类同名方法总结
    89.子类调用父类同名属性和方法
    88.子类调用父类同名属性和方法
    87.子类重写父类的同名属性和方法
    86.多继承
    85.单继承
    84.继承的概念
    day09
    83.魔法方法__del__()
    82.魔法方法__str__()
  • 原文地址:https://www.cnblogs.com/tqxboomzero/p/14256638.html
Copyright © 2011-2022 走看看