题意:
就是有两类人,第一类人的能量值为1,第二类人的能量值为2,你要从中选择三个人且加起来的能量值不能小于5而且这三个人必须是互不相识的。
还需满足的要求:
1.开始时他们互不相识,认识是有传递性的 比如A认识B B认识C 那么A也认识C
2.每次输入两个整数,输入之后代表这两个人认识了,输入n-1次,使得最后每个人都互相认识。
样例输入:
1
5
2 2 2 1 1
4 5
1 4
2 1
3 2
样例输出:
7
7
3
0
0
分析:
把有关系的放在一个集合通过并查集,并且用两个数组分别记录每一个集合中能量值为2和能量值为1的人数。
代码
在这里插入代码片
#include <bits/stdc++.h>
#define ll long long
using namespace std;
int a[100001];
int vis[100001];
int p2[100001]; //该节点的集合下为2的个数
int p1[100001];//该节点的集合下为1的个数
int fa[100001]; //相互之间具有关系的人放在一起
int cnt1=0,cnt2=0;
ll mod=1e9+7;
int fac(int x){
return fa[x]==x?x:fa[x]=fac(fa[x]);
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0), cout.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
cnt1=0,cnt2=0;
for (int i=1;i<=n;i++){
cin>>a[i];
if(a[i]==1){
p1[i]=1;
p2[i]=0;
fa[i]=i;
cnt1++;
}
else{
p1[i]=0;
p2[i]=1;
fa[i]=i;
cnt2++;
}
}
ll ans=(cnt1*cnt2*(cnt2-1)/2%mod+cnt2*(cnt2-1)*(cnt2-2)/6%mod)%mod;
// C cnt2中取3 cnt2中取2cnt1 中取1
cout<<ans<<endl;
for (int i=0;i<n-1;i++){
//每次在上一次的ans上减去先增加的关系中不满足的情况
//增加新关系之后 相对于上一次减去增加的新关系中不符合要求的
//考虑再新增加一个关系x y之后,相对于上一次的关系,
// 分为三部分,一部分为fa[x],fa[y],还有一部分就是剩余的
// 相对于上一次的所有情况中,增加这个关系之后,不满足的选择有四种,
// 从fa[x] 集合中选择一个 fx[y]集合中选择一个 剩余的集合中选择一个
// 分别选择的能力值为2 2 1
// 2 1 2 1 2 2 2 2 2
//如果你认为从只从fa[x]或fa[y]集合中选择三个满足能力值大于5的三个人也为
// 不满足的情况,那这个认为是错误的,因为fa[x]这个集合里面的元素都是有关系的,
//而在上一次求得的ans中没有加入这种情况,只是加入新增加的四种情况。
int x,y;
cin>>x>>y;
int fx=fac(x);
int fy=fac(y);
ll k=0; //k 为不符合条件的那些人
k= (ll)p2[fx]*p2[fy]*(cnt1-p1[fx]-p1[fy])%mod;// 2 2 1
k=(k+(ll)p2[fx]*p1[fy]*(cnt2-p2[fx]-p2[fy]))%mod; // 2 1 2
k=(k+(ll)p1[fx]*p2[fy]*(cnt2-p2[fx]-p2[fy]))%mod; // 1 2 2
k=(k+(ll)p2[fx]*p2[fy]*(cnt2-p2[fx]-p2[fy]))%mod; // 2 2 2
ans=(ans-k+mod)%mod;
cout<<ans<<endl;
fa[fy]=fx;
p1[fx]+=p1[fy];
p2[fx]+=p2[fy];
p1[fy]=0,p2[fy]=0;
}
}
}