洛谷-【图论2-1】树1

P5908 猫猫和企鹅

题目描述

王国里有 n 个居住区,它们之间有 n−1 条道路相连,并且保证从每个居住区出发都可以到达任何一个居住区,并且每条道路的长度都为 1。

除 1 号居住区外,每个居住区住着一个小企鹅,有一天一只猫猫从 1 号居住区出发,想要去拜访一些小企鹅。可是猫猫非常的懒,它只愿意去距离它不大于 d 的小企鹅们。

猫猫非常的懒,因此希望你告诉他,他可以拜访多少只小企鹅。

输入格式

第一行两个整数 n,d,意义如题所述。

第二行开始,共 n−1 行,每行两个整数 u,v,表示居民区 u 和 v 之间存在道路。

输出格式

一行一个整数,表示猫猫可以拜访多少只小企鹅。

输入输出样例

输入 #1复制

复制代码
5 1
1 2
1 3
2 4
3 5

输出 #1复制

复制代码
2

说明/提示

对于 100% 的数据,满足 1≤n,d≤105,保证所有居民区从 1 开始标号。

实现代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
vector <int> g[123456];
bool vis[123456];
int ans,n,d;
void dfs(int now,int dis)
{
	vis[now]=1;
	if(dis==d) return;
	for(int i=0;i<g[now].size();i++)
	{
		if(!vis[g[now][i]])
		{
			dfs(g[now][i],dis+1);
			ans++;
		}
	}
}
int main()
{
	cin>>n>>d;
	for(int i=1;i<n;i++)
	{
		int a,b;
		cin>>a>>b;
		g[a].push_back(b);
		g[b].push_back(a);
	}
	dfs(1,0);
	cout<<ans;
	return 0;
}

P1099 [NOIP 2007 提高组] 树网的核

题目描述

设 T=(V,E,W) 是一个无圈且连通的无向图(也称为无根树),每条边都有正整数的权,我们称 T 为树网(treenetwork),其中 V,E 分别表示结点与边的集合,W 表示各边长度的集合,并设 T 有 n 个结点。

路径:树网中任何两结点 a,b 都存在唯一的一条简单路径,用 d(a,b) 表示以 a,b 为端点的路径的长度,它是该路径上各边长度之和。我们称 d(a,b) 为 a,b 两结点间的距离。

D(v,P)=min{d(v,u)}, u 为路径 P 上的结点。

树网的直径:树网中最长的路径称为树网的直径。对于给定的树网 T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。

偏心距 ECC(F):树网 T 中距路径 F 最远的结点到路径 F 的距离,即

ECC(F)=max{D(v,F),v∈V}

任务:对于给定的树网 T=(V,E,W) 和非负整数 s,求一个路径 F,他是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过 s(可以等于 s),使偏心距 ECC(F) 最小。我们称这个路径为树网 T=(V,E,W) 的核(Core)。必要时,F 可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。

下面的图给出了树网的一个实例。图中,A−B 与 A−C 是两条直径,长度均为 20。点 W 是树网的中心,EF 边的长度为 5。如果指定 s=11,则树网的核为路径DEFG(也可以取为路径DEF),偏心距为 8。如果指定 s=0(或 s=1、s=2),则树网的核为结点 F,偏心距为 12。

输入格式

共 n 行。

第 1 行,两个正整数 n 和 s,中间用一个空格隔开。其中 n 为树网结点的个数,s 为树网的核的长度的上界。设结点编号以此为 1,2...,n。

从第 2 行到第 n 行,每行给出 3 个用空格隔开的正整数 u,v,w,依次表示每一条边的两个端点编号和长度。例如,2 4 7 表示连接结点 2 与 4 的边的长度为 7。

输出格式

一个非负整数,为指定意义下的最小偏心距。

输入输出样例

输入 #1复制

复制代码
5 2
1 2 5
2 3 2
2 4 4
2 5 3

输出 #1复制

复制代码
5

输入 #2复制

复制代码
8 6
1 3 2
2 3 2 
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3

输出 #2复制

复制代码
5

说明/提示

  • 对于 40% 的数据,保证 n≤15。
  • 对于 70% 的数据,保证 n≤80。
  • 对于 100% 的数据,保证 2≤n≤300,0≤s≤103,1≤u,v≤n,0≤w≤103。

NOIP2007 提高组第四题

实现代码:

cpp 复制代码
#include <cstdio>
#include <map>
#include <vector>
#include <algorithm>
#define mp make_pair
#define pr pair<int,int>
#define maxn 500010
#define ll long long
using namespace std;

struct ed{
	ll u,nex,w;
}e[maxn*2];
ll n,s,st,fir[maxn];
ll ans,A,B,D,l,r,d[maxn];
ll f[maxn][21],dd[maxn],v[maxn],w[maxn],F,minn;
ll p,q,l1,l2;

void add(ll x,ll y,ll w)
{
	e[++st].u=y; e[st].nex=fir[x]; e[fir[x]=st].w=w;
	e[++st].u=x; e[st].nex=fir[y]; e[fir[y]=st].w=w;
}

void dfs(ll u,ll fa,ll w)
{
	d[u]=d[fa]+w; if (d[u]>ans) ans=d[u],l=u;
	for (ll i=fir[u],ax=0;i;i=e[i].nex)
	{
		ll v=e[i].u,w=e[i].w;
		if (v==fa) continue;  dfs(v,u,w);
	}
}

void dfsl(ll u,ll fa)
{
	f[u][0]=fa; dd[u]=dd[fa]+1; 
	for (ll i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1];
	for (ll i=fir[u];i;i=e[i].nex) if (e[i].u!=fa) dfsl(e[i].u,u),w[e[i].u]=e[i].w;
}

void Fdis(ll u) { while (u) v[u]=1,u=f[u][0]; } 

ll lca(ll x,ll y)
{
	if (dd[x]>dd[y]) swap(x,y);
	for (ll i=20;i>=0;i--)
		if (dd[y]-(1<<i)>=dd[x]) y=f[y][i];
	if (x==y) return x;
	for (ll i=20;i>=0;i--)
		if (f[y][i]!=f[x][i]) y=f[y][i],x=f[x][i];
	return f[x][0];
}

ll dismin() 
{
	dfsl(A,0); Fdis(B); 
	for (ll i=1;i<=n;i++)
	if (!v[i])
	{
		ll L=lca(i,B); 
		minn=max(minn,d[i]-d[L]);
	}
	return minn;
}

ll drop(ll u,ll fa,ll x)
{
	for (ll i=fir[u];i;i=e[i].nex)
	if (v[e[i].u]) {
		if (e[i].u==fa) continue;
		l1+=e[i].w; if (l1>x) return u;
		else return drop(e[i].u,u,x);
	}
}



ll up(ll u,ll x)
{
	while (u!=A&&l2+w[u]<=x) l2+=w[u],u=f[u][0]; 
	return u;
}

bool check(ll x)
{
	l1=l2=0; p=drop(A,0,x); q=up(B,x); 
	return (d[q]-d[p]<=s);
}

int main()
{
	scanf("%lld%lld",&n,&s);
	for (ll i=1,x,y,w;i<n;i++)
	scanf("%lld%lld%lld",&x,&y,&w),add(x,y,w);
	dfs(1,0,0); ans=0; r=l; dfs(l,0,0); 
	A=r; B=l; D=ans;
	l=dismin(); r=D; 
	while (l<r)
	{
		ll mid=(l+r)/2; 
		if (check(mid)) r=mid; else l=mid+1;
	}
	printf("%lld\n",l);
} 

P1395 会议

题目描述

有一个村庄居住着 n 个村民,有 n−1 条路径使得这 n 个村民的家联通,每条路径的长度都为 1。现在村长希望在某个村民家中召开一场会议,村长希望所有村民到会议地点的距离之和最小,那么村长应该要把会议地点设置在哪个村民的家中,并且这个距离总和最小是多少?若有多个节点都满足条件,则选择节点编号最小的那个点。

输入格式

第一行,一个数 n,表示有 n 个村民。

接下来 n−1 行,每行两个数字 a 和 b,表示村民 a 的家和村民 b 的家之间存在一条路径。

输出格式

一行输出两个数字 x 和 y。

x 表示村长将会在哪个村民家中举办会议。

y 表示距离之和的最小值。

输入输出样例

输入 #1复制

复制代码
4
1 2 
2 3 
3 4 

输出 #1复制

复制代码
2 4

说明/提示

数据范围

对于 70% 数据 n≤103。

对于 100% 数据 n≤5×104。

实现代码:

cpp 复制代码
#include<cstdio>
#define N 50005

int d[N];
int f[N];
int n,cnt;
int size[N];
bool vis[N];
int head[N];

struct Edge{
    int to,nxt;
}edge[N<<1];

void add(int x,int y){
    edge[++cnt].to=y;
    edge[cnt].nxt=head[x];
    head[x]=cnt;
}

void dfs1(int now){
    size[now]=1;
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(d[to]) continue;
        d[to]=d[now]+1;
        dfs1(to);
        size[now]+=size[to];
    }
}

void dfs(int now,int fa){
    f[now]=f[fa]+n-2*size[now];
    for(int i=head[now];i;i=edge[i].nxt){
        int to=edge[i].to;
        if(to==fa) continue;
        dfs(to,now);
    }
}

signed main(){
    scanf("%d",&n);
    for(int x,y,i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    d[1]=1;
    dfs1(1);
    int maxn=0,idx=1;
    for(int i=1;i<=n;i++) maxn+=d[i];
    maxn-=n;
    f[1]=maxn;
    for(int i=head[1];i;i=edge[i].nxt){
        int to=edge[i].to;
        dfs(to,1);
    }
    for(int i=2;i<=n;i++){
        if(f[i]<maxn) maxn=f[i],idx=i;
    }
    printf("%d %d",idx,maxn);
    return 0;
}

P3379 【模板】最近公共祖先(LCA)

题目描述

如题,给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先。

输入格式

第一行包含三个正整数 N,M,S,分别表示树的结点个数、询问的个数和树根结点的序号。

接下来 N−1 行每行包含两个正整数 x,y,表示 x 结点和 y 结点之间有一条直接连接的边(数据保证可以构成树)。

接下来 M 行每行包含两个正整数 a,b,表示询问 a 结点和 b 结点的最近公共祖先。

输出格式

输出包含 M 行,每行包含一个正整数,依次为每一个询问的结果。

输入输出样例

输入 #1复制

复制代码
5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

输出 #1复制

复制代码
4
4
1
4
4

说明/提示

对于 30% 的数据,N≤10,M≤10。

对于 70% 的数据,N≤10000,M≤10000。

对于 100% 的数据,1≤N,M≤5×105,1≤x,y,a,b≤N,不保证 a=b。

样例说明:

该树结构如下:

第一次询问:2,4 的最近公共祖先,故为 4。

第二次询问:3,2 的最近公共祖先,故为 4。

第三次询问:3,5 的最近公共祖先,故为 1。

第四次询问:1,2 的最近公共祖先,故为 4。

第五次询问:4,5 的最近公共祖先,故为 4。

故输出依次为 4,4,1,4,4。

2021/10/4 数据更新 @fstqwq:应要求加了两组数据卡掉了暴力跳。

实现代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
constexpr int N = 5e5 + 5;
int n, m, R, dn, dfn[N], mi[19][N];
vector<int> e[N];
int get(int x, int y) {return dfn[x] < dfn[y] ? x : y;}
void dfs(int id, int f) {
  mi[0][dfn[id] = ++dn] = f;
  for(int it : e[id]) if(it != f) dfs(it, id); 
}
int lca(int u, int v) {
  if(u == v) return u;
  if((u = dfn[u]) > (v = dfn[v])) swap(u, v);
  int d = __lg(v - u++);
  return get(mi[d][u], mi[d][v - (1 << d) + 1]);
}
int main() {
  scanf("%d %d %d", &n, &m, &R);
  for(int i = 2, u, v; i <= n; i++) {
    scanf("%d %d", &u, &v);
    e[u].push_back(v), e[v].push_back(u);
  }
  dfs(R, 0);
  for(int i = 1; i <= __lg(n); i++)
    for(int j = 1; j + (1 << i) - 1 <= n; j++)
      mi[i][j] = get(mi[i - 1][j], mi[i - 1][j + (1 << i - 1)]);
  for(int i = 1, u, v; i <= m; i++) scanf("%d %d", &u, &v), printf("%d\n", lca(u, v));
  return 0;
}
相关推荐
段ヤシ.1 小时前
回顾Java知识点,面试题汇总Day5(持续更新)
java·开发语言
不会C语言的男孩1 小时前
C++ SLTL编程
java·开发语言·c++
java修仙传1 小时前
Java 实习日记:从业务表关系到节点价格分析接口改造
java·开发语言·实习
qq_452396231 小时前
第十四篇:《JMeter插件扩展:自定义函数与第三方插件》
开发语言·python·jmeter
敲代码的嘎仔1 小时前
力扣高频SQL基础50题详解
开发语言·数据库·笔记·sql·算法·leetcode·后端开发
码农-阿杰1 小时前
Java 线程等待唤醒机制深度解析:synchronized、ReentrantLock、LockSupport 底层实现对比
java·开发语言·c++
赤水无泪1 小时前
Qt 全模块汇总列表
开发语言·qt
小虎牙0072 小时前
面试被问复杂度总懵?这篇指南帮你彻底搞清
算法
yong99902 小时前
MATLAB仿真计算电磁波回波信号的技术路径与实现指南
开发语言·matlab