萌新刚学 OI 一秒
  • 板块P4178 Tree
  • 楼主Iggle_Piggle
  • 当前回复1
  • 已保存回复1
  • 发布时间2023/11/16 20:48
  • 上次更新2023/11/16 21:53:24
查看原帖
萌新刚学 OI 一秒
1094614
Iggle_Piggle楼主2023/11/16 20:48

悬一关,点分治模板题求调。

题意

给定一棵 nn 个节点的树,每条边有边权,求出树上两点距离小于等于 kk 的点对数量。

(我甘愿为调出代码的dalao做牛做马)

我代码的主要思路也列在下面了,但是最后写挂了。(样例测出来是 6)

/*点分治
这里点切树分三种情况讨论:
1. 点对在此点切出的同一子树内
2. 点对其中之一即改“切点”
3. 点对在此点切出的不同子树内 
*/ 
#include<bits/stdc++.h>
using namespace std;
const int N= 4e4+ 5;
int n, k, p[N], q[N];
bool vis[N];
vector< pair< int, int> > e[N<< 1];

int get_size(int u, int fa)
{
	int res= 1;
	for(auto v : e[u])
		if(!vis[v.second] && v.second!= fa)
			res+= get_size(v.second, u);
	return res;
}

int get_root(int u, int fa, int ts, int& root)
{
	int sum= 1, ms= 0;
	for(auto v : e[u])
		if(!vis[v.second] && v.second!= fa)
		{
			int t= get_root(v.second, u, ts, root);
			ms= max(ms, t);
			sum+= t;
		}
	ms= max(ts- sum, ms);
	if(ms<= (ts>> 1)) root= u;
	return sum;
}

void get_dis(int u, int fa, int dis, int& qt)
{
	q[++qt]= dis;
	for(auto v : e[u])
		if(!vis[v.second] && v.second!= fa)
			get_dis(v.second, u, dis+ v.first, qt);
}

int get(int a[], int len)
{
	int res= 0;
	sort(a+ 1, a+ len+ 1);
	for(int i= len, j= 0; i> 0; i--)
	{
		while(j< i && a[j]+ a[i]<= k) j++;
		j= min(i- 1, j);
		res+= j;
	}
	return res;
}

int calc(int u)
{
	int res= 0;
	get_root(u, 0, get_size(u, 0), u);
	vis[u]= 1;
	
	//第三种情况 
	int pt= 0;
	for(auto v : e[u])
		if(!vis[v.second])
		{
			int qt= 0;
			get_dis(v.second, 0, v.first, qt);
			res-= get(q, qt);
			for(int i= 1; i<= qt; i++)
			{
				if(q[i]<= k) res++; //第二种情况
				p[++pt]= q[i];
			}
		}
	res+= get(p, pt);
	
	for(auto v : e[u]) if(!vis[v.second]) res+= calc(v.second); //第一种情况 
	/*因为过来的父节点一定已被 vis 标记,所以只判 vis,就可以不走回去*/
	return res;
}
signed main()
{
	scanf("%d", &n);
	int u, v, w;
	for(int i= 1; i< n; i++)
	{
		scanf("%d%d%d", &u, &v, &w);
		e[u].push_back(make_pair(w, v)), e[v].push_back(make_pair(w, u));
	}
	scanf("%d", &k);
	printf("%d", calc(1));
	return 0;
}
2023/11/16 20:48
加载中...