题意:
给 定 n 个 点 , 有 两 种 点 , 权 值 分 别 为 1 和 2 , 给定n个点,有两种点,权值分别为1和2, 给定n个点,有两种点,权值分别为1和2,
初 始 时 , n 个 点 互 不 相 连 。 初始时,n个点互不相连。 初始时,n个点互不相连。
接 着 会 加 入 n − 1 条 边 , 保 证 每 次 加 入 的 边 的 两 个 端 点 事 先 是 不 相 连 通 的 。 接着会加入n-1条边,保证每次加入的边的两个端点事先是不相连通的。 接着会加入n−1条边,保证每次加入的边的两个端点事先是不相连通的。
要 从 中 选 择 3 个 点 , 满 足 3 个 点 的 权 值 之 和 不 少 于 5 , 且 3 个 点 之 间 互 不 相 连 , 计 算 出 不 同 的 选 择 方 案 的 数 量 。 要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。 要从中选择3个点,满足3个点的权值之和不少于5,且3个点之间互不相连,计算出不同的选择方案的数量。
每 加 入 一 条 边 , 都 要 输 出 当 前 连 通 状 态 下 , 不 同 的 选 择 方 案 的 数 量 。 每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。 每加入一条边,都要输出当前连通状态下,不同的选择方案的数量。
输入:
首 行 输 入 一 个 正 整 数 T ( 1 ≤ T ≤ 10 ) , 表 示 测 试 数 据 的 组 数 。 首行输入一个正整数T(1≤T≤10),表示测试数据的组数。 首行输入一个正整数T(1≤T≤10),表示测试数据的组数。
每 组 数 据 的 首 行 输 入 一 个 正 整 数 n ( 1 ≤ n ≤ 1 0 5 ) , 表 示 点 的 数 量 , 每组数据的首行输入一个正整数n(1≤n≤10^5),表示点的数量, 每组数据的首行输入一个正整数n(1≤n≤105),表示点的数量,
接 着 输 入 n 个 数 , 表 示 每 个 点 的 权 值 ( 1 或 2 ) 。 接着输入n个数,表示每个点的权值(1或2)。 接着输入n个数,表示每个点的权值(1或2)。
最 后 n − 1 行 数 据 , 每 行 包 括 两 个 正 整 数 u i , v i , 表 示 在 u i 和 v i 之 间 建 立 一 条 无 向 边 。 最后n-1行数据,每行包括两个正整数u_i,v_i,表示在u_i和v_i之间建立一条无向边。 最后n−1行数据,每行包括两个正整数ui,vi,表示在ui和vi之间建立一条无向边。
输出:
输 出 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。 ①、要保证权值之和不少于5,那么有两种组合方式:2+2+1或者2+2+2。
② 、 顺 着 题 意 来 计 数 似 乎 不 太 方 便 ( 要 考 虑 缩 点 、 连 通 块 等 等 问 题 ) 。 可 以 反 过 来 思 考 。 ②、顺着题意来计数似乎不太方便(要考虑缩点、连通块等等问题)。可以反过来思考。 ②、顺着题意来计数似乎不太方便(要考虑缩点、连通块等等问题)。可以反过来思考。
我 们 观 察 样 例 , 发 现 当 边 的 数 量 逐 渐 增 多 时 , 方 案 数 一 定 会 递 减 到 0 。 \qquad我们观察样例,发现当边的数量逐渐增多时,方案数一定会递减到0。 我们观察样例,发现当边的数量逐渐增多时,方案数一定会递减到0。
Ⅰ 、 初 始 状 态 的 计 算 : 假 设 权 值 为 1 的 点 的 数 量 为 c n t 1 , 权 值 为 2 的 点 的 数 量 为 c n t 2 , \qquadⅠ、初始状态的计算:假设权值为1的点的数量为cnt_1,权值为2的点的数量为cnt_2, Ⅰ、初始状态的计算:假设权值为1的点的数量为cnt1,权值为2的点的数量为cnt2,
此 时 的 不 同 方 案 的 总 数 : 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之间添加一条边, 由于每次加边合并都是对两个连通块进行操作,假设现在u和v之间添加一条边,
因 此 , 我 们 将 n 个 点 分 为 3 个 部 分 : u 所 在 连 通 块 G u 、 v 所 在 的 连 通 块 G v 、 其 他 剩 余 点 G r 。 \qquad\quad\ \ 因此,我们将n个点分为3个部分:u所在连通块G_u、v所在的连通块G_v、其他剩余点G_r。 因此,我们将n个点分为3个部分:u所在连通块Gu、v所在的连通块Gv、其他剩余点Gr。
对 于 每 个 连 通 块 , 我 们 用 并 查 集 维 护 , 同 时 额 外 维 护 两 个 数 组 p 1 和 p 2 , \qquad\quad\ \ 对于每个连通块,我们用并查集维护,同时额外维护两个数组p_1和p_2, 对于每个连通块,我们用并查集维护,同时额外维护两个数组p1和p2,
p 1 [ i ] 表 示 以 i 为 根 节 点 的 连 通 块 中 , 权 值 为 1 的 点 的 数 量 , p 2 [ i ] 同 理 。 \qquad\quad\ \ p_1[i]表示以i为根节点的连通块中,权值为1的点的数量,p_2[i]同理。 p1[i]表示以i为根节点的连通块中,权值为1的点的数量,p2[i]同理。
记 u 所 在 连 通 块 的 根 节 点 为 p u , v 所 在 连 通 块 的 根 节 点 为 p v , \qquad\quad\ \ 记u所在连通块的根节点为pu,v所在连通块的根节点为pv, 记u所在连通块的根节点为pu,v所在连通块的根节点为pv,
那 么 合 并 u 和 v 所 在 的 两 个 连 通 块 后 , 新 增 的 不 合 法 的 方 案 必 是 分 别 从 三 个 部 分 中 各 取 一 个 点 , \qquad\quad\ \ 那么合并u和v所在的两个连通块后,新增的不合法的方案必是分别从三个部分中各取一个点, 那么合并u和v所在的两个连通块后,新增的不合法的方案必是分别从三个部分中各取一个点,
有 四 种 可 能 : \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)、从Gu中选择一个权值为2的点,从Gv中选择一个权值为2的点,从Gr中选择一个权值为1的点。
方 案 总 数 为 : 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]×(cnt1−p1[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)、从Gu中选择一个权值为2的点,从Gv中选择一个权值为2的点,从Gr中选择一个权值为2的点。
方 案 总 数 为 : 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]×(cnt2−p2[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)、从Gu中选择一个权值为2的点,从Gv中选择一个权值为1的点,从Gr中选择一个权值为2的点。
方 案 总 数 为 : 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]×(cnt2−p2[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)、从Gu中选择一个权值为1的点,从Gv中选择一个权值为2的点,从Gr中选择一个权值为2的点。
方 案 总 数 为 : 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]×(cnt2−p2[pu]−p2[pv])
那 么 , 每 次 我 们 用 上 一 阶 段 的 a n s 减 去 当 前 阶 段 四 种 情 况 不 合 法 方 案 数 之 和 即 可 。 那么,每次我们用上一阶段的ans减去当前阶段四种情况不合法方案数之和即可。 那么,每次我们用上一阶段的ans减去当前阶段四种情况不合法方案数之和即可。
最 后 合 并 u 和 v 所 在 的 连 通 块 。 最后合并u和v所在的连通块。 最后合并u和v所在的连通块。
注意:
计 算 过 程 中 有 好 几 处 可 能 爆 i n t , 比 如 计 算 k 时 。 计算过程中有好几处可能爆int,比如计算k时。 计算过程中有好几处可能爆int,比如计算k时。
代码:
#include
#include
#include
#include
#include
#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;
}