传送门
题意:给定 n n n个结点 n − 1 n-1 n−1条边的树,求结点的所有子集 ( 区 间 [ l , r ] ) (区间[l,r]) (区间[l,r])的连通块的和。
思路:考虑最开始为 n n n个孤立点的所有子集的连通块的和,再减去每条边对于连通块的贡献。
显然每个点就是一个连通块。
考虑包含 1 1 1的子集(区间): l ≤ 1 , r ≥ 1 l\leq1,r\geq1 l≤1,r≥1, l l l有1种选择, r r r有 n − 1 + 1 n-1+1 n−1+1种选择。
即 1 × ( n − 1 + 1 ) = n 1\times(n-1+1)=n 1×(n−1+1)=n种选择。
包含 2 2 2子集(区间): l ≤ 2 , r ≥ 2 l\leq2,r\geq2 l≤2,r≥2, l l l有 2 2 2种选择, r r r有 n − 2 + 1 n-2+1 n−2+1种选择。
即 2 × ( n − 2 + 1 ) = 2 × ( n − 1 ) 2\times(n-2+1)=2\times(n-1) 2×(n−2+1)=2×(n−1)
依次类推:
可得总连通块数:
∑ i = 1 n i × ( n − i + 1 ) = ( n + 1 ) × ∑ i = 1 n i − ∑ i = 1 n i 2 = ( n + 1 ) × n ( n + 1 ) 2 − n ( n + 1 ) ( n + 2 ) 6 = n ( n + 1 ) ( 3 n + 3 − ( n + 2 ) ) 6 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum\limits_{i=1}^ni\times(n-i+1)\\=(n+1)\times\sum\limits_{i=1}^ni-\sum\limits_{i=1}^ni^2\\=(n+1)\times \dfrac{n(n+1)}{2}-\dfrac{n(n+1)(n+2)}{6}\\=\dfrac{n(n+1)(3n+3-(n+2))}{6}\\=\dfrac{n(n+1)(2n+1)}{6} i=1∑ni×(n−i+1)=(n+1)×i=1∑ni−i=1∑ni2=(n+1)×2n(n+1)−6n(n+1)(n+2)=6n(n+1)(3n+3−(n+2))=6n(n+1)(2n+1)
显然对于边 ( u , v ) (u,v) (u,v),(假设 u < v u
l ≤ u , r ≥ v l\leq u,r\geq v l≤u,r≥v,一共 u × ( n − v + 1 ) u\times(n-v+1) u×(n−v+1)个子集,每个子集因为 u , v u,v u,v的相连会把 u , v u,v u,v两个连通块减为 1 1 1个。所以答案要减去 u × ( n − v + 1 ) u\times(n-v+1) u×(n−v+1)。
所以一边读入一般减去贡献即可。
时间复杂度: O ( n ) O(n) O(n)
#include
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e6+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a) memset(a,0,sizeof a)
#define lx x<<1
#define rx x<<1|1
#define reg register
#define PII pair
#define fi first
#define se second
int main(){
int n;
scanf("%d",&n);
ll ans=1LL*n*(n+1)*(n+2)/6;
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
if(u>v) swap(u,v);
ans-=1LL*u*(n-v+1);
}
printf("%lld\n",ans);
return 0;
}