能力提升综合题单Part 8.3.1 二叉树&Part 8.3.2 树的直径

1.P1087 FBI树

递归求解,判断是否为’0’串或者’1’串即可

#include<bits/stdc++.h>
using namespace std;
string a;
int n;
void chuan(string x){
	if(x.length()>0){
	int len=x.length();
	chuan(x.substr(0,len/2));
	chuan(x.substr(len/2,len-1));
	if (x == string(x.length(), '0')) cout<<'B';
    else if (x == string(x.length(), '1')) cout<<'I';
	else{
		cout<<'F';
	}
	}
}
int main(){
	cin>>n>>a;
	chuan(a);
	return 0;
}

2.P1030 求先序排列

给出了中序和后序排列,求先序排列
由这两种输出方式的性质可知,根在后序的最后输出,所以我们找到根为分界线,不断递归为两个子树进行求解

#include<bits/stdc++.h>
using namespace std;
string a,b;
void dfs(string x,string y){
	if(x.length()>0){
	int len=y.length();
	cout<<y[len-1];
	char ch=y[len-1];
	int t=x.find(ch);
	dfs(x.substr(0,t),y.substr(0,t));
	dfs(x.substr(t+1,len-1),y.substr(t,len-t-1));}
}
int main(){
	cin>>a>>b;
	dfs(a,b);
	return 0;
}

3.P1305 新二叉树

给一棵树,输出先序遍历,直接递归即可

#include<bits/stdc++.h>
using namespace std;
struct node{
	char lc,rc;
}t[200];
char t1,tt;
void xianxu(char x){
	if(x=='*') return;
	cout<<x;
	xianxu(t[x].lc);
	xianxu(t[x].rc);
}
int main(){
	int n;
	cin>>n;
	cin>>t1;
	cin>>t[t1].lc;
	cin>>t[t1].rc;
	for(int i=2;i<=n;i++){
		cin>>tt;
		cin>>t[tt].lc;
		cin>>t[tt].rc;
	}
	xianxu(t1);
	return 0;
}

4.P1229 遍历问题

给出先序和后序遍历,求这棵树最多有多少种不同中序遍历的情况
中序+x 可以求出这棵树
转化一下题意就是求有多少个子树只有一个儿子

#include<bits/stdc++.h>
using namespace std;
string a,b;
int main(){
	cin>>a;
	cin>>b;
	int ans=0;
	int n=a.length();
	for(int i=0;i<n;i++){
		for(int j=1;j<n;j++){
			if(a[i]==b[j]&&a[i+1]==b[j-1]){
				ans++;
			}
		}
	}
	printf("%d",1<<ans);
}

5.P5018 对称二叉树

遍历每一个节点,递归判断子树是否对称,并更新最大值

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
	int l,r,w;
}t[1000005];
int n;
bool same(int x,int y){
	if(x==-1&&y==-1)return true;
	if(x==-1||y==-1)return false;
	if(t[x].w!=t[y].w)return false;
	return same(t[x].l,t[y].r)&&same(t[x].r,t[y].l);
}
int ct(int x){
	return x==-1?0:ct(t[x].l)+ct(t[x].r)+1;
}
signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%lld",&t[i].w);
	}
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&t[i].l,&t[i].r);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		if(same(i,i))ans=max(ans,ct(i));
	}
	cout<<ans;
	return 0;
}

6.P5597 【XR-4】复读

咕咕咕待补

7.P2195 HXY造公园

操作1,输出该点所在的树的直径
操作2,将u,v所在的树连接,并且要求连接后的树的直径尽量小
怎么实现操作2?选两个直径的中点进行合并

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
int f[maxn],c[maxn],vis[maxn],n,m,q,g[maxn],d[maxn],len;
vector<int>e[maxn];
int find(int x){
	if(x==f[x])return x;
	return f[x]=find(f[x]);
}
void dfs(int x,int fa){
	int m1=-1,m2=-1;
	for(int i=0;i<e[x].size();++i){
		int y=e[x][i];
		if(y==fa)continue;
		dfs(y,x);
		int tmp=d[y]+1;
		d[x]=max(d[x],tmp);
		if(tmp>m1)m2=m1,m1=tmp;
		else if(tmp>m2)m2=tmp;
	}
	g[x]=max(max(0,m1+m2),max(m1,m2));
	len=max(len,g[x]);
}
void calc(int x)
{
	len=0;
	dfs(x,0);
	c[x]=len;
}
int main(){
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++){
		f[i]=i;
	}
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		e[u].push_back(v);
		e[v].push_back(u);
		f[find(u)]=find(v);
	}
	for(int i=1;i<=n;i++){
		if(f[i]!=i||vis[i])continue;
		calc(i);
		vis[i]=1;
	}
	int tt,u,v;
	while(q--){
		scanf("%d",&tt);
		if(tt==1){
			scanf("%d",&u);
			printf("%d\n",c[find(u)]);
			continue;
		}
		scanf("%d%d",&u,&v);
		int x=find(u);
		int y=find(v);
		if(x==y)continue;
		int t=((c[x]+1)/2)+((c[y]+1)/2)+1;
		t=max(t,max(c[x],c[y]));
		f[find(x)]=find(y);
		c[find(x)]=t;
	}
	return 0;
}

8.P3629 [APIO2010]巡逻

有两种情形,k=1的时候只需要找到直径,连接两端即可
k=2时,如果连接的边经过了第一条路径,就要再走一遍,所以把第一条已经连好的路上权值取负
x-y-(-y)=x
由于dfs求直径不能处理负数,所以用dp求,也是这个题的精髓所在,需要用dfs求直径来记录路径,用dp来处理负权

#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
int head[maxm],dis[maxm],n,k,cnt=1,pos,st,ed;
int f[maxm],vis[maxm];
struct edge{
	int v,w,nex;
}e[maxm];
inline void add(int u,int v){
	e[++cnt].v=v;
	e[cnt].w=1;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
void change(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		if(vis[v]){
			e[i].w=-1;
			e[i^1].w=-1;
		}
		change(v,u);
	}
	return;
}
int l2;
void dp(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dp(v,u);
		l2=max(dis[v]+dis[u]+e[i].w,l2);
		dis[u]=max(dis[u],dis[v]+e[i].w);
	}
	return;
}
int main(){
	cin>>n>>k;
	int u,v;
	for(int i=1;i<=n-1;i++){
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	st=1;
	pos=1;
	dis[1]=0;
	dfs(st,0);
	st=pos;
	dis[st]=0;
	dfs(st,0);
	ed=pos;
	int l1=dis[ed];
	int r=ed;
	while(r){
		vis[r]=1;
		r=f[r];
	}
	change(st,0);
	memset(dis,0,sizeof(dis));
	dp(st,0);
	if(k==1)l2=1;
	int ans=n*2-l1-l2;
	cout<<ans;
	return 0;
}

9.P5536 【XR-3】核心城市

在树中间选一些点,使其他点到这些点的最大距离最小
先求直径,再求中点,中点一定要被选中,然后以中点为根建树,进行一次dfs,统计每个点向下延伸的最大深度,对子节点进行排序,显然第k+1个节点+1即为答案(因为记录的是最大延伸,需要加上自己与核心城市的距离1)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n,k,head[maxn],f[maxn],st,ed,pos,dis[maxn],cnt,vis[maxn],len[maxn];
struct edge{
	int v,w,nex;
}e[maxn];
vector<int>ve;
inline void add(int u,int v){
	e[++cnt].v=v;
	e[cnt].w=1;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dis[v]=dis[u]+1;
		dfs(v,u);
	}
	return;
}
void dfs3(int sn,int fa){
	len[sn]=0;
	for(int i=head[sn];i;i=e[i].nex){
		int v=e[i].v;
		if(v!=fa){
			dfs3(v,sn);
			len[sn]=max(len[sn],len[v]+1);
		}
	}
}
bool cmp(int x,int y){
	return x>y;
}
int main(){
	cin>>n>>k;
	for(int i=1;i<=n-1;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	st=1,pos=1,dis[1]=0;
	dfs(st,0);
	st=pos,dis[st]=0;
	dfs(st,0);
	ed=pos;
	int l1=dis[ed];
	int st2=ed;
	for(int i=1;i<=(l1+1)/2;i++){
		st2=f[st2];
	}
	dfs3(st2,0);
	sort(len+1,len+1+n,cmp);
	cout<<len[k+1]+1;
	return 0;
}

10.P1099 树网的核

P2491 [SDOI2011]消防
U89620 树网的核加强版
这两题是数据加强板(三倍经验)
前提是用最优解做的
直接上数据最强的一个版本的代码

大体的思路是这样的,先用dfs求出直径,然后从端点进行遍历,选择每一段小于等于s的路径,求出两端点到这段路径的最小距离,
然后求出每个直径上的点到这个点的最小距离,跟ans进行比较

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int n,s,head[maxn],f[maxn],st,ed,pos,dis[maxn],cnt,vis[maxn],len[maxn];
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
struct edge{
	int v,w,nex;
}e[maxn];
inline void add(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
void dfs2(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
int main(){
	cin>>n>>s;
	int u,v,w;
	for(int i=1;i<=n-1;i++){
		u=read(),v=read(),w=read();
		add(u,v,w);
		add(v,u,w);
	}
	st=1,pos=1,dis[1]=0;
	dfs(st,0);
	st=pos,dis[st]=0;
	dfs(st,0);
	ed=pos;
	int ans=0x3f3f3f3f;
	int tmp=ed;
	for(int i=ed;i;i=f[i]){
		while(f[tmp]&&dis[i]-dis[f[tmp]]<=s){
			tmp=f[tmp];
		}
		ans=min(ans,max(dis[ed]-dis[i],dis[tmp]));
	}
	for(int i=ed;i;i=f[i]){
		vis[i]=1;
		dis[i]=0;
	}
//	cout<<ed<<endl;
//	for(int i=1;i<=n;i++){
//		printf("%d %d  vis:%d\n",i,f[i],vis[i]);
//	}
	for(int i=ed;i;i=f[i]){
		dfs2(i,0);
	}
	
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			ans=max(ans,dis[i]);
		}
	}
	cout<<ans;
	return 0;
}

11.P4408 [NOI2003]逃学的小孩

求非直径上的点到直径的两端点的最大值

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=4e5+5;
int dis2[maxn],dis3[maxn];
int n,m,head[maxn],f[maxn],st,ed,pos,dis[maxn],cnt,vis[maxn],len[maxn];
inline int read()
{
    int x=0,k=1; char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')k=-1;c=getchar();}
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*k;
}
struct edge{
	int v,w,nex;
}e[maxn];
inline void add(int u,int v,int w){
	e[++cnt].v=v;
	e[cnt].w=w;
	e[cnt].nex=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa){
	if(dis[u]>dis[pos]||pos==st)pos=u;
	f[u]=fa;
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa)continue;
		dis[v]=dis[u]+e[i].w;
		dfs(v,u);
	}
	return;
}
void dfs2(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis2[v]=dis2[u]+e[i].w;
		dfs2(v,u);
	}
	return;
}
void dfs3(int u,int fa){
	for(int i=head[u];i;i=e[i].nex){
		int v=e[i].v;
		if(v==fa||vis[v]==1)continue;
		dis3[v]=dis3[u]+e[i].w;
		dfs3(v,u);
	}
	return;
}
signed main(){
	cin>>n>>m;
	int u,v,w;
	for(int i=1;i<=m;i++){
		u=read(),v=read(),w=read();
		add(u,v,w);
		add(v,u,w);
	}
	st=1,pos=1,dis[1]=0;
	dfs(st,0);
	st=pos,dis[st]=0;
	dfs(st,0);
	ed=pos;
	int ans=dis[ed];
	dfs2(st,0);
	dfs3(ed,0);
//	for(int i=1;i<=n;i++){
//		printf("i=%d dis2 %d dis3 %d\n",i,dis2[i],dis3[i]);
//	}
	int tmp=0;
	for(int i=1;i<=n;i++){
		int d=min(dis2[i],dis3[i]);
		tmp=max(tmp,d);
	}
	cout<<ans+tmp;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值