zoukankan      html  css  js  c++  java
  • 「JOISC 2020 Day2」有趣的 Joitter 交友

    「JOISC 2020 Day2」有趣的 Joitter 交友

    传送门

    Loj

    题解

    首先我们考虑,有着双向边的两个点可以合并成为一个点,这是比较显然的(.此时只有单向边在我们的新图里面.

    仔细构思一下,不难发现在一条边加入的时候,有如下三种情况:

    1. 两个点已经在一起了,这个时候啥都不用做.
    2. 两个点之间没有任何的关系,那么只需要记个答案就行了.
    3. 两个点之间有着反向连接的关系,这个时候需要重新计算答案然后合并这两个节点.

    计算答案考虑一个连通块的贡献是(siz_u*(siz_u-1)+siz_u*in_u),其中(siz)为这个连通块的大小,(in)是入边数量.

    唯一需要考虑的点在于可能会影响很多下,要递归合并.

    代码

    #include<algorithm>
    #include<iostream>
    #include<string.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<math.h>
    #include<bitset>
    #include<queue>
    #include<map>
    #include<set>
    using namespace std;
    #define file(a) freopen(a".in","r",stdin);freopen(a".out","w",stdout)
    #define ll long long
    #define mp make_pair
    #define re register
    typedef pair<int,int> pii;
    inline int gi()
    {
    	int f=1,sum=0;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch<='9'&&ch>='0'){sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    	return f*sum;
    }
    const int N=600010;
    int siz[N],f[N],n,m;ll ans;
    set<int>in[N];set<pii>out[N];
    int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
    void solve(int u,int v)
    {
    	int fu=find(u),fv=find(v);
    	if(fu==fv||in[fv].find(u)!=in[fv].end())return;
    	if(out[fv].lower_bound(mp(fu,0))->first!=fu)
    	{
    		ans+=siz[fv];
    		in[fv].insert(u);
    		out[fu].insert(mp(fv,u));
    		return;
    	}
    	if(in[fu].size()+out[fu].size()<in[fv].size()+out[fv].size())swap(fu,fv);
    	vector<int>IN;vector<pii>OUT;
    	for(auto it:out[fv])
    	{
    		int _=it.first,__=it.second;
    		in[_].erase(__);ans-=siz[_];
    		if(_!=fu)OUT.push_back(it);
    	}
    	out[fv].clear();
    	ans+=2ll*siz[fu]*siz[fv]-in[fv].size()*siz[fv]+in[fu].size()*siz[fv];
    	for(auto it:in[fv])
    	{
    		int _=find(it);
    		out[_].erase(mp(fv,it));
    		if(_!=fu)IN.push_back(it);
    	}
    	in[fv].clear();
    	f[fv]=fu;siz[fu]+=siz[fv];
    	for(auto it:IN)solve(it,fu);
    	for(auto it:OUT)solve(it.second,it.first);
    }
    int main()
    {
    	n=gi();m=gi();
    	for(int i=1;i<=n;i++)siz[i]=1,f[i]=i;
    	while(m--)
    	{
    		int u=gi(),v=gi();
    		solve(u,v);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java中RuntimeException和Exception
    RuntimeException和Exception的区别
    Spring事务异常回滚
    iOS 卖票中多线程分析;
    凝视转换(部分)
    HDU 5386 Cover(模拟)
    iOS开发之软键盘使用小技巧
    【每日算法】高速幂
    CKEditor高级编辑器
    iOS开发 剖析网易新闻标签栏视图切换(addChildViewController属性介绍)
  • 原文地址:https://www.cnblogs.com/fexuile/p/12956774.html
Copyright © 2011-2022 走看看