2019牛客暑期多校训练营(第九场) All men are brothers 并查集+思维

[All men are brothers](https://ac.nowcoder.com/acm/contest/889/E)

在这里插入图片描述在这里插入图片描述
样例1:

6 6
1 2
3 4
4 5
3 5
3 6
2 4

输出:
15
9
4
0
0
0
0

样例2:
100000 0
输出
4166416671249975000

题意:
n个人,m个相互认识的关系,问从n个人中选择4个人,使其相互不认识的方案是多少;
分析:
最最暴力的方法就是每次都去查询n次,那么这样的复杂度是1e10,

因为每一次都去合并的话,答案是每次都是递减的,那么就看一下每次合并对答案的影响是多少,先看没有合并的时候,一共是有4种方案:
设需要合并的集合是t1与t2,那么剩下的集合用s来表示

  1. t1选1个,t2选1个,s选2个
  2. t1选1个,s选3个
  3. t2选1个,s选3个
  4. s选4个

然后再看合并之后的:
1.t1和t2中选1个,s选3个(这其实就是上面的2+3的方案数)
2.s选4个

这样看来就是只有第一种情况减少了,那么这样就可以减去第一种情况的方案数不就是答案了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cmath>
#include<cstring>
#include<set>
#include<queue>
#include<stack>
#include<map>
#define rep(i,a,b) for(int i=a;i<=b;i++)
typedef long long ll;
using namespace std;
const int N=1e5+10;
const int INF=0x3f3f3f3f;
ll c[N][6],ans4,ans2,sum[N];
int f[N];
int n,m;
int findf(int v){
    if(v==f[v])
        return v;
    else
        f[v]=findf(f[v]);
    return f[v];
}
void merg(int x,int y){
    int t1=findf(x),t2=findf(y);
    if(t1!=t2){
      f[t2]=t1;
      ll s=n-sum[t1]-sum[t2];//不属于t1和t2的人数
      ll t=ans2-s*sum[t1]-s*sum[t2]-sum[t1]*sum[t2];//从不属于t1和t2的人数选择两个人的方案数
      ans4-=sum[t1]*sum[t2]*t;
      ans2=t+s*(sum[t1]+sum[t2]);//合并之后的选择两个人的方案数
        sum[t1]+=sum[t2];
        sum[t2]=0;
      if(ans4<0) ans4=0;
      printf("%lld\n",ans4);
    }else
    printf("%lld\n",ans4);
}
void init(){
    for(int i=1;i<=100000;i++) f[i]=i,sum[i]=1;
    c[1][0]=c[1][1]=1;
    for(int i=2;i<=n;i++){
        c[i][0]=1;
        for(int j=1;j<=4;j++)
            c[i][j]=c[i-1][j]+c[i-1][j-1];
    }
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif // ONLINE_JUDGE
    scanf("%d%d",&n,&m);
    init();
    ans2=c[n][2];
    printf("%lld\n",ans4=c[n][4]);
    rep(i,1,m){int x,y;
        scanf("%d%d",&x,&y);
        merg(x,y);
    }
    return 0;
}


发布了229 篇原创文章 · 获赞 17 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/99716077
今日推荐