考虑计算每条树边出现在哪些区间了,但是这样不太好统计,补集转换一下计算每条树边没有出现的区间的个数
那么用set维护一下每棵子树中的点的标号,如果一个区间里的元素都不在这个set里或者都在这个set里,那么这个点到父亲的边都不在这个区间里
启发式合并一下就可以了
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=100010;
int n,cnt,G[N]; ll w[N];
struct edge{
int t,nx;
}E[N<<1];
set<pii> S[N];
inline void addedge(int x,int y){
E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
E[++cnt].t=x; E[cnt].nx=G[y]; G[y]=cnt;
}
inline ll C(int l,int r){
return 1LL*(r-l+1)*(r-l)/2+r-l+1;
}
inline void ins(int x,pii y){
set<pii> &c=S[x];
int l,r;
if(c.empty() || *c.begin()>y) l=1;
else l=(--c.upper_bound(y))->se+1;
if(c.empty() || *c.rbegin()<y) r=n;
else r=c.upper_bound(y)->fi-1;
w[x]-=C(l,r);
if(l<y.fi) w[x]+=C(l,y.fi-1);
else{
if(!c.empty() && *c.begin()<y){
set<pii>::iterator it=c.upper_bound(y); it--;
y.fi=it->fi; w[x]-=C(it->fi,it->se); c.erase(it);
}
}
if(r>y.se) w[x]+=C(y.se+1,r);
else{
if(!c.empty() && *c.rbegin()>y){
set<pii>::iterator it=c.upper_bound(y);
y.se=it->se; w[x]-=C(it->fi,it->se); c.erase(it);
}
}
w[x]+=C(y.fi,y.se);
c.insert(y);
}
void dfs(int x,int f){
w[x]=C(1,n); ins(x,pii(x,x));
for(int i=G[x];i;i=E[i].nx)
if(E[i].t!=f){
dfs(E[i].t,x);
if(S[E[i].t].size()>S[x].size())
swap(S[E[i].t],S[x]),w[x]=w[E[i].t];
for(set<pii>::iterator it=S[E[i].t].begin();it!=S[E[i].t].end();it++) ins(x,*it);
}
}
int main(){
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
scanf("%d",&n);
for(int i=1,x,y;i<n;i++)
scanf("%d%d",&x,&y),addedge(x,y);
dfs(1,0);
ll ans=0;
for(int i=2;i<=n;i++) ans+=1LL*n*(n+1)/2-w[i];
printf("%lld\n",ans);
return 0;
}