- P1087 FBI树
- P1030 求先序排列
- P1305 新二叉树
- P1229 遍历问题
- P5018 对称二叉树
- P5597 【XR-4】复读
- P2195 HXY造公园
- P3629 [APIO2010]巡逻
- P5536 【XR-3】核心城市
- P1099 树网的核
- P4408 [NOI2003]逃学的小孩
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;
}