ABC.173.F - Intervals on Tree
传送门
题意:给定n个结点n−1条边的树,求结点的所有子集(区间[l,r])的连通块的和。
思路:考虑最开始为n个孤立点的所有子集的连通块的和,再减去每条边对于连通块的贡献。
显然每个点就是一个连通块。
考虑包含1的子集(区间): l≤1,r≥1,l有1种选择,r有n−1+1种选择。
即1×(n−1+1)=n种选择。
包含2子集(区间):l≤2,r≥2,l有2种选择,r有n−2+1种选择。
即2×(n−2+1)=2×(n−1)
依次类推:
可得总连通块数:
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,因为区间l≤r),显然包含边(u,v)的子集(区间)。
l≤u,r≥v,一共u×(n−v+1)个子集,每个子集因为u,v的相连会把u,v两个连通块减为1个。所以答案要减去u×(n−v+1)。
所以一边读入一边减去贡献即可。
时间复杂度:O(n)
#include<bits/stdc++.h>
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<int,int>
#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;
}