Daimayuan Online Judge #505. 三角果计数——思维+树上技巧

题目链接

思路

  • 因为在树上,那么大概率存在些树上的一些规律技巧。 我们通过作图来看,发现只有三个点ABC在同一条路径上,就不能组成三角形。比如下图的ABD三个点,因为距离AB+BD=AD。不满足形成三角形的条件。只要不在同一条路径上的三个点就能形成三角形,那么我们枚举中转点。 比如下图的B,在它的三个子树里面每一个任选一个点出来就能组成三角形。

在这里插入图片描述

  • 对于不止三个子树的点,我们任选三个子树,再累加上答案。但是这样暴力枚举会超时。
  • 因为是选任意三个子树出来,我们可以这样想,当前枚举的子树A, 已经走过的子树B,还没有走过的子树C。我们每次从这三个区域各个选一个点出来,那么就能做到不重不漏的计数了。
    在这里插入图片描述

AC代码

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x; i<=y; ++i)
#define per(i,x,y) for(int i=x; i>=y; --i)
#define pushk push_back
#define popk pop_back
#define mem(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define ll long long
#define lp p<<1
#define rp p<<1|1
using namespace std;
const int N = 1e6+9;
int sz[N];
ll ans;
vector<int> g[N];
int n;
void dfs(int u,int fa) {
	if(g[u].size()==1) {
		sz[u]++;
		return ;
	}
	for(auto &it:g[u]) {
		if(it==fa) continue;
		dfs(it,u);
		ans+=1ll*sz[u]*(n-sz[u]-sz[it]-1)*sz[it];
		sz[u]+=sz[it]; 
	}
	sz[u]++;
}
int main() {


	cin>>n;
	rep(i,1,n-1) {
		int u,v,w;
		scanf("%d %d %d",&u,&v,&w);
		g[u].pushk(v);
		g[v].pushk(u);
	}
	dfs(1,0);
	cout<<ans<<'\n';
	return 0;
}

参考

我最先想出来了思路,但是不知道怎么优化枚举,提交上去T了一个点,优化思路是参考这两位大佬。
知乎大佬
csdn大佬

posted @ 2022-08-28 08:43  翔村亲亲鸟  阅读(29)  评论(0编辑  收藏  举报