题意:给定n个人,第i个人为1 表示是读者,2表示为程序员,ACM赛制是三人一组,规定三人至少有两个为程序员,而且三个人互相不认识。一开始所有人都是互相不认识。然后依次给出n-1条朋友关系(关系满足传递)使得所有人都互相认识。求给出第i条朋友关系时,可以三人一组组队的方案数是多少。答案模1e9+7.
图片说明

分析:并查集,增加一个新边,对原有方案减少了多少。我们并查集维护一个连通块里2和1的人数各有多少。增加新边相当于要减去从(2,2,1),(2,2,2)这两种的方案。
那么选人左右连通块都是可能会选的:
左边选1的人,右边选2 的人 方案(1,2,2)
左边选2的人,右边选1 的人 方案(1,2,2)
左边选2的人,右边选2 的人 方案(2,2,2)
第三个人的选人集合( cnti-s[x][i]-s[y][i] )

类似题目( https://ac.nowcoder.com/acm/contest/889/E
题解: E https://blog.nowcoder.net/n/860ec8e0ecc1412fae37ce93292b430b

#include<bits/stdc++.h>
#define sscc ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;

const ll mod=1e9+7;
const ll maxn=1e5+5;

ll f[maxn];




ll a[maxn],b[maxn];
ll p[maxn],s[maxn][3];

int get_fa( int x ) { return f[x]==x ? x : f[x]=get_fa(f[x]); } 


int main()
{
    sscc; 

    int t;
    cin>>t;
    while( t-- )
    {
        int n;
        cin>>n;
        ll cnt1=0;
        ll cnt2=0;
        memset(s,0,sizeof(s));
        for( int i=1;i<=n;i++ )
        {
            cin>>p[i];
            int x=p[i];
            f[i]=i;
            if( x==2 ) cnt2++,s[i][2]++;
            else cnt1++,s[i][1]++;
        }


        ll ans=( cnt2*(cnt2-1)/2*cnt1%mod+((cnt2-2)*cnt2*(cnt2-1))/6 )%mod;
        cout<<ans<<"\n";
        int m=n-1;

        while( m-- )
        {
            int x,y;
            cin>>x>>y;
            x=get_fa(x);
            y=get_fa(y);


            ans-=1ll*s[x][1]*s[y][2]*(cnt2-s[y][2]-s[x][2])%mod+
            1ll*s[x][2]*s[y][1]*(cnt2-s[y][2]-s[x][2])%mod+
            1ll*s[x][2]*s[y][2]*(cnt1-s[y][1]-s[x][1])%mod+
            1ll*s[x][2]*s[y][2]*(cnt2-s[y][2]-s[x][2])%mod;

            ans=(ans+4*mod)%mod;

            f[x]=y;
            s[y][1]+=s[x][1];
            s[y][2]+=s[x][2];

            cout<<ans<<"\n";
        }
    }
}