组合数学 + 并查集 - Little W and Contest - HDU 6795

组合数学 + 并查集 - Little W and Contest - HDU 6795

2020 Multi-University Training Contest 3

题意:

给 定 n 个 点 , 有 两 种 点 , 权 值 分 别 为 1 和 2 , 给定n个点,有两种点,权值分别为1和2, n12

初 始 时 , n 个 点 互 不 相 连 。 初始时,n个点互不相连。 n

接 着 会 加 入 n − 1 条 边 , 保 证 每 次 加 入 的 边 的 两 个 端 点 事 先 是 不 相 连 通 的 。 接着会加入n-1条边,保证每次加入的边的两个端点事先是不相连通的。 n1

要 从 中 选 择 3 个 点 , 满 足 3 个 点 的 权 值 之 和 不 少 于 5 , 且 3 个 点 之 间 互 不 相 连 , 计 算 出 不 同 的 选 择 方 案 的 数 量 。 要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。 3353

每 加 入 一 条 边 , 都 要 输 出 当 前 连 通 状 态 下 , 不 同 的 选 择 方 案 的 数 量 。 每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。

输入:

首 行 输 入 一 个 正 整 数 T ( 1 ≤ T ≤ 10 ) , 表 示 测 试 数 据 的 组 数 。 首行输入一个正整数T(1≤T≤10),表示测试数据的组数。 T(1T10)

每 组 数 据 的 首 行 输 入 一 个 正 整 数 n ( 1 ≤ n ≤ 1 0 5 ) , 表 示 点 的 数 量 , 每组数据的首行输入一个正整数n(1≤n≤10^5),表示点的数量, n(1n105)

接 着 输 入 n 个 数 , 表 示 每 个 点 的 权 值 ( 1 或 2 ) 。 接着输入n个数,表示每个点的权值(1或2)。 n(12)

最 后 n − 1 行 数 据 , 每 行 包 括 两 个 正 整 数 u i , v i , 表 示 在 u i 和 v i 之 间 建 立 一 条 无 向 边 。 最后n-1行数据,每行包括两个正整数u_i,v_i,表示在u_i和v_i之间建立一条无向边。 n1ui,viuivi

输出:

输 出 n 行 , 表 示 从 初 始 阶 段 到 添 加 最 后 一 条 边 的 每 个 阶 段 , 不 同 方 案 的 总 数 。 输出n行,表示从初始阶段到添加最后一条边的每个阶段,不同方案的总数。 n

Sample Input

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

Sample Output

7
7
3
0
0

分析:

① 、 要 保 证 权 值 之 和 不 少 于 5 , 那 么 有 两 种 组 合 方 式 : 2 + 2 + 1 或 者 2 + 2 + 2 。 ①、要保证权值之和不少于5,那么有两种组合方式:2+2+1或者2+2+2。 52+2+12+2+2

② 、 顺 着 题 意 来 计 数 似 乎 不 太 方 便 ( 要 考 虑 缩 点 、 连 通 块 等 等 问 题 ) 。 可 以 反 过 来 思 考 。 ②、顺着题意来计数似乎不太方便(要考虑缩点、连通块等等问题)。可以反过来思考。 便()

我 们 观 察 样 例 , 发 现 当 边 的 数 量 逐 渐 增 多 时 , 方 案 数 一 定 会 递 减 到 0 。 \qquad我们观察样例,发现当边的数量逐渐增多时,方案数一定会递减到0。 0

Ⅰ 、 初 始 状 态 的 计 算 : 假 设 权 值 为 1 的 点 的 数 量 为 c n t 1 , 权 值 为 2 的 点 的 数 量 为 c n t 2 , \qquadⅠ、初始状态的计算:假设权值为1的点的数量为cnt_1,权值为2的点的数量为cnt_2, 1cnt12cnt2

   此 时 的 不 同 方 案 的 总 数 : a n s = C c n t 2 2 × C c n t 1 1 + C c n t 2 3 \qquad\quad\ \ 此时的不同方案的总数:ans=C_{cnt_2}^2×C_{cnt_1}^1+C_{cnt_2}^3   :ans=Ccnt22×Ccnt11+Ccnt23

Ⅱ 、 我 们 考 虑 每 次 添 加 一 条 边 后 , 会 有 哪 一 部 分 的 方 案 会 变 成 不 合 法 的 方 案 , \qquadⅡ、我们考虑每次添加一条边后,会有哪一部分的方案会变成不合法的方案,

   由 于 每 次 加 边 合 并 都 是 对 两 个 连 通 块 进 行 操 作 , 假 设 现 在 u 和 v 之 间 添 加 一 条 边 , \qquad\quad\ \ 由于每次加边合并都是对两个连通块进行操作,假设现在u和v之间添加一条边,   uv

   因 此 , 我 们 将 n 个 点 分 为 3 个 部 分 : u 所 在 连 通 块 G u 、 v 所 在 的 连 通 块 G v 、 其 他 剩 余 点 G r 。 \qquad\quad\ \ 因此,我们将n个点分为3个部分:u所在连通块G_u、v所在的连通块G_v、其他剩余点G_r。   n3uGuvGvGr

   对 于 每 个 连 通 块 , 我 们 用 并 查 集 维 护 , 同 时 额 外 维 护 两 个 数 组 p 1 和 p 2 , \qquad\quad\ \ 对于每个连通块,我们用并查集维护,同时额外维护两个数组p_1和p_2,   p1p2

   p 1 [ i ] 表 示 以 i 为 根 节 点 的 连 通 块 中 , 权 值 为 1 的 点 的 数 量 , p 2 [ i ] 同 理 。 \qquad\quad\ \ p_1[i]表示以i为根节点的连通块中,权值为1的点的数量,p_2[i]同理。   p1[i]i1p2[i]

   记 u 所 在 连 通 块 的 根 节 点 为 p u , v 所 在 连 通 块 的 根 节 点 为 p v , \qquad\quad\ \ 记u所在连通块的根节点为pu,v所在连通块的根节点为pv,   upuvpv

   那 么 合 并 u 和 v 所 在 的 两 个 连 通 块 后 , 新 增 的 不 合 法 的 方 案 必 是 分 别 从 三 个 部 分 中 各 取 一 个 点 , \qquad\quad\ \ 那么合并u和v所在的两个连通块后,新增的不合法的方案必是分别从三个部分中各取一个点,   uv

   有 四 种 可 能 : \qquad\quad\ \ 有四种可能:   

   ( 1 ) 、 从 G u 中 选 择 一 个 权 值 为 2 的 点 , 从 G v 中 选 择 一 个 权 值 为 2 的 点 , 从 G r 中 选 择 一 个 权 值 为 1 的 点 。 \qquad\quad\ \ (1)、从G_u中选择一个权值为2的点,从G_v中选择一个权值为2的点,从G_r中选择一个权值为1的点。   (1)Gu2Gv2Gr1

    方 案 总 数 为 : p 2 [ p u ] × p 2 [ p v ] × ( c n t 1 − p 1 [ p u ] − p 1 [ p v ] ) \qquad\qquad\quad \ \ \ 方案总数为:p_2[pu]×p_2[pv]×(cnt_1-p_1[pu]-p_1[pv])    p2[pu]×p2[pv]×(cnt1p1[pu]p1[pv])

   ( 2 ) 、 从 G u 中 选 择 一 个 权 值 为 2 的 点 , 从 G v 中 选 择 一 个 权 值 为 2 的 点 , 从 G r 中 选 择 一 个 权 值 为 2 的 点 。 \qquad\quad\ \ (2)、从G_u中选择一个权值为2的点,从G_v中选择一个权值为2的点,从G_r中选择一个权值为2的点。   (2)Gu2Gv2Gr2

    方 案 总 数 为 : p 2 [ p u ] × p 2 [ p v ] × ( c n t 2 − p 2 [ p u ] − p 2 [ p v ] ) \qquad\qquad\quad \ \ \ 方案总数为:p_2[pu]×p_2[pv]×(cnt_2-p_2[pu]-p_2[pv])    p2[pu]×p2[pv]×(cnt2p2[pu]p2[pv])

   ( 3 ) 、 从 G u 中 选 择 一 个 权 值 为 2 的 点 , 从 G v 中 选 择 一 个 权 值 为 1 的 点 , 从 G r 中 选 择 一 个 权 值 为 2 的 点 。 \qquad\quad\ \ (3)、从G_u中选择一个权值为2的点,从G_v中选择一个权值为1的点,从G_r中选择一个权值为2的点。   (3)Gu2Gv1Gr2

    方 案 总 数 为 : p 2 [ p u ] × p 1 [ p v ] × ( c n t 2 − p 2 [ p u ] − p 2 [ p v ] ) \qquad\qquad\quad \ \ \ 方案总数为:p_2[pu]×p_1[pv]×(cnt_2-p_2[pu]-p_2[pv])    p2[pu]×p1[pv]×(cnt2p2[pu]p2[pv])

   ( 4 ) 、 从 G u 中 选 择 一 个 权 值 为 1 的 点 , 从 G v 中 选 择 一 个 权 值 为 2 的 点 , 从 G r 中 选 择 一 个 权 值 为 2 的 点 。 \qquad\quad\ \ (4)、从G_u中选择一个权值为1的点,从G_v中选择一个权值为2的点,从G_r中选择一个权值为2的点。   (4)Gu1Gv2Gr2

    方 案 总 数 为 : p 1 [ p u ] × p 2 [ p v ] × ( c n t 2 − p 2 [ p u ] − p 2 [ p v ] ) \qquad\qquad\quad \ \ \ 方案总数为:p_1[pu]×p_2[pv]×(cnt_2-p_2[pu]-p_2[pv])    p1[pu]×p2[pv]×(cnt2p2[pu]p2[pv])

那 么 , 每 次 我 们 用 上 一 阶 段 的 a n s 减 去 当 前 阶 段 四 种 情 况 不 合 法 方 案 数 之 和 即 可 。 那么,每次我们用上一阶段的ans减去当前阶段四种情况不合法方案数之和即可。 ans

最 后 合 并 u 和 v 所 在 的 连 通 块 。 最后合并u和v所在的连通块。 uv

注意:

计 算 过 程 中 有 好 几 处 可 能 爆 i n t , 比 如 计 算 k 时 。 计算过程中有好几处可能爆int,比如计算k时。 intk

代码:

#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>

#define ll long long

using namespace std;

const int N=1e5+10, mod=1e9+7;

int T,n;
int w[N];
int cnt[3];
int p[N];
int p1[N],p2[N];

int Find(int x)
{
    if(p[x]!=x) p[x]=Find(p[x]);
    return p[x];
}

int C_2(int n)
{
    if(n<2) return 0;
    return (ll)(n-1)*n/2%mod;
}

int C_3(int n)
{
    if(n<3) return 0;
    return (ll)(n)*(n-1)*(n-2)/6%mod;
}

int main()
{  
    cin>>T;
    while(T--)
    {
        scanf("%d",&n);
        memset(cnt,0,sizeof cnt);
        for(int i=1;i<=n;i++) 
        {
            scanf("%d",&w[i]);p[i]=i;
            if(w[i]==1) {p1[i]=1, p2[i]=0;cnt[1]++;}
            else {p2[i]=1, p1[i]=0;cnt[2]++;}
        }
        int ans=(C_3(cnt[2])+(ll)C_2(cnt[2])*cnt[1]%mod)%mod;
        printf("%d\n",ans);
        
        int u,v;
        for(int i=0;i<n-1;i++)
        {
            int k=0;
            scanf("%d%d",&u,&v);
            int pu=Find(u), pv=Find(v);

            k=(k+(ll)p1[pu]*p2[pv]*(cnt[2]-p2[pu]-p2[pv]))%mod;
    
            k=(k+(ll)p2[pu]*p1[pv]*(cnt[2]-p2[pu]-p2[pv]))%mod;
            
            k=(k+(ll)p2[pu]*p2[pv]*(cnt[2]-p2[pu]-p2[pv]))%mod;
            
            k=(k+(ll)p2[pu]*p2[pv]*(cnt[1]-p1[pu]-p1[pv]))%mod;
            
            ans=(ans-k+mod)%mod;
            printf("%d\n",ans);
            p[pv]=pu;
            p1[pu]+=p1[pv], p2[pu]+=p2[pv];
            p1[pv]=0, p2[pv]=0;
        }
    }
    
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值