HDU 4705 立方和拆解
题意
给一棵树,找出所有的(A,B,C)三元组的数量,满足ABC不能同时被一条路径覆盖
思路
-
首先可以明确的是,对于每个入度大于等于3的点(也就是有两个或以上儿子的节点),对答案都有贡献,并且这个贡献是任意三个与其相连的部分的权值乘积的加和。(由于每个节点的“分叉位置”不用,所以不会出现重复记录的现象)
-
也就是说,从根节点(我们取1节点为根)开始,进行到一棵以某节点为根的子树时,设这棵子树权值为 q 0 q_0 q0, 而此节点下面的子树权值为 q 1 , q 2 ⋅ ⋅ ⋅ q s q_1, q_2 ··· q_s q1,q2⋅⋅⋅qs, 我们要算的就是
∑ i = 0 s − 2 ∑ j = i + 1 s − 1 ∑ k = j + 1 s q i ∗ q j ∗ q k \sum_{i = 0}^{s - 2}{\sum_{j = i + 1}^{s - 1}{\sum_{k = j + 1}^{s}{q_i * q_j * q_k}}} i=0∑s−2j=i+1∑s−1k=j+1∑sqi∗qj∗qk
- 但是我们肯定不能n^3来算,这个时候可以模仿序列和的平方化任意两数乘积加和的公式:
( ( ∑ i = 0 s q i ) 2 − ∑ i = 0 s q i 2 ) / 2 = ∑ i = 0 s − 1 ∑ j = i + 1 s q i ∗ q j ((\sum_{i = 0}^{s}{q_i})^2 - \sum_{i = 0}^{s}{q_i ^ 2}) / 2 = \sum_{i = 0}^{s - 1}{\sum_{j = i + 1}^{s}{q_i * q_j}} ((i=0∑sqi)2−i=0∑sqi2)/2=i=0∑s−1j=i+1∑sqi∗qj
来写出序列和的立方化任意三数乘积加和的公式:
( ( ∑ i = 0 s q i ) 3 − 3 ∗ ∑ i = 0 s q i 2 ∗ ∑ i = 0 s q i + 2 ∗ ∑ i = 0 s q i 3 ) / 6 = ∑ i = 0 s − 2 ∑ j = i + 1 s − 1 ∑ k = j + 1 s q i ∗ q j ∗ q k ((\sum_{i = 0}^{s}{q_i})^3 - 3 * \sum_{i = 0}^{s}{q_i ^ 2} *\sum_{i = 0}^{s}{q_i} + 2 * \sum_{i = 0}^{s}{q_i ^ 3}) / 6 = \sum_{i = 0}^{s - 2}{\sum_{j = i + 1}^{s - 1}{\sum_{k = j + 1}^{s}{q_i * q_j * q_k}}} ((i=0∑sqi)3−3∗i=0∑sqi2∗i=0∑sqi+2∗i=0∑sqi3)/6=i=0∑s−2j=i+1∑s−1k=j+1∑sqi∗qj∗qk
这样就可以O(n)搜索算出答案了
AC代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int n;
int vv[200005], nex[200005], fir[100005];
long long sz[100005];
long long ans = 0;
void dfs1(int o, int fa)
{
sz[o] = 1;
for (int i = fir[o]; i; i = nex[i])
{
if (vv[i] != fa)
{
dfs1(vv[i], o);
sz[o] += sz[vv[i]];
}
}
}
void dfs(int o, int fa)
{
long long ext = n - sz[o];
long long c3 = ext * ext * ext;
long long c2 = ext * ext;
long long c1 = ext;
int cnt = 0;
for (int i = fir[o]; i; i = nex[i])
{
if (vv[i] != fa)
{
++cnt;
c3 += sz[vv[i]] * sz[vv[i]] * sz[vv[i]];
c2 += sz[vv[i]] * sz[vv[i]];
c1 += sz[vv[i]];
dfs(vv[i], o);
}
}
if (cnt >= 2)
{
ans += (c1 * c1 * c1 - 3 * c2 * c1 + 2 * c3) / 6;
}
}
int main()
{
while (scanf("%d", &n) == 1)
{
memset(nex, 0, sizeof(int) * (2 * n + 2));
memset(fir, 0, sizeof(int) * (n + 2));
memset(sz, 0, sizeof(long long) * (n + 2));
ans = 0;
for (int i = 1; i < n; ++i)
{
int u;
scanf("%d%d", &u, &vv[i]);
vv[i + n] = u;
nex[i] = fir[u];
fir[u] = i;
nex[i + n] = fir[vv[i]];
fir[vv[i]] = i + n;
}
dfs1(1, 0);
dfs(1, 0);
printf("%lld\n", ans);
}
return 0;
}