题解 CF2063E Triangle Tree
年级里面有大佬用神奇启发式合并写的,还好我不会这么复杂的算法。
题意
给定一棵
设函数
- 当
不为 的祖先,且 不为 的祖先时,存在多少个整数 使得边长为 、 、 能成为一个三角形。 - 否则函数值为
。
最后需要求出:
分析
根据三角形边长的限制,可以得到:
简单来说两边之和大于第三边,两边之差小于第三边。
把距离函数拆成深度,设
然后就可以表示出
后面绝对值这一部分是 [ABC186D] Sum of difference,可以把每个深度的个数加入到一个桶中,统计有多少个数比它小或大。
现在考虑这个
设
这个东西可以前缀和优化做到线性。
最后对于每一个询问还需要减
时间复杂度和空间复杂度均为
代码
//the code is from chenjh
#include<bits/stdc++.h>
#define MAXN 300003
using namespace std;
typedef long long LL;
int n;
vector<int> G[MAXN];
LL ans1=0,ans2=0,ans3=0;
int dep[MAXN],sz[MAXN],a[MAXN];
void dfs(const int u,const int FA){
++a[dep[u]=dep[FA]+1],sz[u]=1;
ans1+=(n-1ll)*dep[u];
int x=0;
LL y=0;//前缀和统计 LCA 为当前点的点对个数。
for(const int v:G[u])if(v!=FA){
dfs(v,u),sz[u]+=sz[v];
y+=(LL)sz[v]*x,x+=sz[v];
}
ans1-=2*(y+sz[u]-1)*dep[u],ans3+=sz[u]-1;//减去 2d_{lca},排除祖先的情况。
}
void solve(){
scanf("%d",&n);
for(int i=1;i<=n;i++) G[i].clear(),a[i]=0;
for(int i=1,u,v;i<n;i++){
scanf("%d%d",&u,&v);
G[u].push_back(v),G[v].push_back(u);
}
ans1=ans2=ans3=0;
dfs(1,0);
for(int i=1,x=0;i<=n;x+=a[i++]) ans2+=(LL)i*a[i]*(x-(n-a[i]-x));//处理绝对值部分。
printf("%lld\n",ans1-ans2+ans3-n*(n-1ll)/2);
}
int main(){
int T;scanf("%d",&T);
while(T--) solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】