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;
}