Little W and Contest(三,学习总结)

本文解析了一道涉及并查集的竞赛题目,通过将人员分成不同连通块,使用并查集算法解决三人组合的问题,确保至少两人具有特定技能且彼此间不相识。文章详细阐述了解题思路和代码实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述
题意:n个人互不认识,经过一次介绍,两人认识,且a对b认识,b对c认识,则a对c认识,每个人都具有技能1或2,从n个人中选择三个人参加比赛,且至少有两个具有技能2,且三个人互补认识。
思路:利用并查集,将这n个人分成三种连通快中,每次从不同块中去取不满足的情况,存在四种可能,每次用前一次的减去不满足的情况个数,得到最终ans。
主要是看了这个理解的:
https://blog.youkuaiyun.com/njuptACMcxk/article/details/107641773
在写的过程中注意防止负数的出现,还有进行下一次例子的时候注意某些变量的初始化。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int N = 1e5+10;
int tcase,n,i,n1,n2,k,ans,j=1,u,v,pu,pv,x;
//int sto[N];
int p1[N],p2[N],p[N];
int Find(int xx)//并查集find函数 
{
    if(p[xx]!=xx) 
	p[xx]=Find(p[xx]);
    return p[xx];
}
int main(){
	scanf("%d",&tcase);
	while(tcase--){
		scanf("%d",&n);
		n1 = n2 = 0;
		for(i=1;i<=n;i++){//输入 
			scanf("%d",&x); p[i]=i;//记录各连通快 
			if(x==1){
				n1++;p1[i]=1;p2[i]=0; 
			}
			else{
				n2++;p2[i]=1;p1[i]=0;/*p1[i]表示以i为根节点的连通块中,
				权值为1的点的数量,p2[i]同理*/
			}
		}
		//首先计算都不认识的时候
		ans = ((ll)n2*(n2-1)*(n2-2)/6%mod + (ll)n1*n2%mod*(n2-1)/2%mod)%mod;//都不认识时的组合个数,利用组合公式
      	cout<<ans<<endl;
		for(i=1;i<n;i++){//输入 
		        k=0; 
			    scanf("%d%d",&u,&v);
		        int pu=Find(u), pv=Find(v);//寻找u,v的根节点
				//输入u,v之后不满足的情况包含四种
				//①1 2 2 
				k=(k+(ll)p1[pu]*p2[pv]*(n2-p2[pu]-p2[pv]))%mod;
				//②2 1 2 
                k=(k+(ll)p2[pu]*p1[pv]*(n2-p2[pu]-p2[pv]))%mod;
                //2 2 2 
                k=(k+(ll)p2[pu]*p2[pv]*(n2-p2[pu]-p2[pv]))%mod;
                //2 2 1 
                k=(k+(ll)p2[pu]*p2[pv]*(n1-p1[pu]-p1[pv]))%mod; 
                
                ans = (ans - k + mod)%mod;
				//sto[j++] = ans;
				p[pv]=pu;
                p1[pu]+=p1[pv]; p2[pu]+=p2[pv];
                p1[pv]=0; p2[pv]=0;
                cout<<ans<<endl;
	      }
	     //for(i=0;i<j;i++){
		//printf("%d\n",sto[i]);
		  // sto[i]=0;
	    // }
  }
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值