链接
题意:给定一颗无根树让你选择两个点,让其他所有点到距离最近的选定点的最大距离最小,输出选定的两个点和这个最大的距离
思路:
方法1
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#define ll long long
using namespace std;
const int maxn=2e5+10,maxm=500+10;
int t,n;
vector<int> e[maxn];
int depth[maxn],fa[maxn];
int get_far(int s)
{
int far=0;
depth[s]=0;
fa[s]=-1;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u=q.front();q.pop();
far=u;
for(auto v: e[u])
{
if(v==fa[u]) continue;
depth[v]=depth[u]+1;
fa[v]=u;
q.push(v);
}
}
return far;
}
int get_center(int x)
{
int far=get_far(x);
far=get_far(far);
int t=depth[far]/2;
while(t--) far=fa[far];
return far;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i) e[i].clear();
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
int center=get_center(1);
int father=fa[center];
for(int i=0;i<e[center].size();++i)
{
if(e[center][i]==father)
{
e[center].erase(e[center].begin()+i);
break;
}
}
for(int i=0;i<e[father].size();++i)
{
if(e[father][i]==center)
{
e[father].erase(e[father].begin()+i);
break;
}
}
int p1=get_center(fa[center]);
int dis1=depth[p1];
int p2=get_center(center);
int dis2=depth[p2];
int dis=max(dis1,dis2);
printf("%d %d %d\n",dis,p1,p2);
}
return 0;
}
方法2
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int t,n,far,p1,p2;
vector<int> e[maxn];
bool visit[maxn][2];
int fa[maxn],depth[maxn],dis[maxn];;
int get_far(int s)
{
int far=0;
depth[s]=0;
fa[s]=-1;
queue<int> q;
q.push(s);
while(!q.empty())
{
int u=q.front();
q.pop();
far=u;
for(auto v: e[u])
{
if(v==fa[u]) continue;
depth[v]=depth[u]+1;
fa[v]=u;
q.push(v);
}
}
return far;
}
int movek(int u,int k)
{
while(k--&&u!=-1)
u=fa[u];
return u;
}
void bfs(int s,int k,int t)
{
dis[s]=0;
queue<int> q;
q.push(s);
visit[s][t]=1;
while(!q.empty())
{
int u=q.front();q.pop();
if(dis[u]==k) continue;
for(auto v: e[u])
{
if(visit[v][t]) continue;
visit[v][t]=1;
dis[v]=dis[u]+1;
q.push(v);
}
}
}
bool check(int k)
{
p1=p2=-1;
memset(visit,0,sizeof(visit));
int v1=movek(far,k);
if(v1==-1)
{
p1=1,p2=2;
return 1;
}
p1=v1;
bfs(v1,k,0);
int u2=-1;
for(int i=1;i<=n;++i)
{
if(visit[i][0]) continue;
if(u2==-1||depth[i]>depth[u2]) u2=i;
}
if(u2==-1)
{
p1=v1;
if(v1+1<=n) p2=v1+1;
else p2=v1-1;
return 1;
}
int v2=movek(u2,k);
if(v2==-1)
{
if(p1==1) p2=2;
else p2=1;
return 1;
}
bfs(v2,k,1);
p2=v2;
for(int i=1;i<=n;++i)
if(!visit[i][0]&&!visit[i][1])
return 0;
return 1;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i) e[i].clear();
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
far=get_far(1);
int l=0,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
check(l);
printf("%d %d %d\n",l,p1,p2);
}
return 0;
}
链接:https://www.codechef.com/problems/DTREE
题意:给定一颗树,请你求出删除每一个节点之后的森林中的最大直径是多少 ( 1 ≤ n ≤ 100000 ) (1 \le n\le 100000) (1≤n≤100000)
思路:
#include <cstdio>
#include <vector>
#include <cstring>
#include <queue>
#define ll long long
using namespace std;
const int maxn=1e5+10,maxm=500+10;
int t,n;
int depth[maxn];//子树中最大深度
int dis1[maxn];//与自己相连的最远距离
int dis2[maxn];//子树中的最远+次远:最大子树直径
int ans[maxn];
vector<int> e[maxn];
void dfs1(int u,int fa=-1)
{
for(auto v: e[u])
{
if(v==fa) continue;
dfs1(v,u);
depth[u]=max(depth[u],depth[v]+1);
}
}
void dfs2(int u,int fa=-1)
{
int mx1=-1,mx2=-1;
for(auto v: e[u])
{
if(v==fa) continue;
dis1[v]=dis1[u]+1;
if(mx1<=depth[v])
{
mx2=mx1;
mx1=depth[v];
}
else mx2=max(mx2,depth[v]);
}
for(auto v: e[u])
{
if(v==fa) continue;
if(mx1==depth[v]) dis1[v]=max(dis1[v],mx2+2);
else dis1[v]=max(dis1[v],mx1+2);
dfs2(v,u);
dis2[u]=max(dis2[u],dis2[v]);
}
dis2[u]=max(dis2[u],mx1+mx2+2);
}
void dfs3(int u,int fa=-1,int p=0)
{
int mx1=-1,mx2=-1,mx3=-1,mxd1=0,mxd2=0;
for(auto v: e[u])
{
if(v==fa) continue;
if(mx1<=depth[v])
{
mx3=mx2,mx2=mx1;
mx1=depth[v];
}
else if(mx2<=depth[v])
{
mx3=mx2;
mx2=depth[v];
}
else mx3=max(mx3,depth[v]);
if(mxd1<=dis2[v])
{
mxd2=mxd1;
mxd1=dis2[v];
}
else mxd2=max(mxd2,dis2[v]);
}
ans[u]=max(ans[u],mxd1);
for(auto v: e[u])
{
if(v==fa) continue;
int mx=p;
if(mxd1==dis2[v]) mx=max(mx,mxd2);
else mx=max(mx,mxd1);
if(depth[v]==mx1)
{
mx=max(mx,mx2+mx3+2);
mx=max(mx,mx2+1+dis1[u]);
}
else if(depth[v]==mx2)
{
mx=max(mx,mx1+mx3+2);
mx=max(mx,mx1+1+dis1[u]);
}
else
{
mx=max(mx,mx1+mx2+2);
mx=max(mx,mx1+1+dis1[u]);
}
ans[v]=max(ans[v],mx);
dfs3(v,u,mx);
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
depth[i]=dis1[i]=dis2[i]=ans[i]=0;
e[i].clear();
}
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
dfs1(1);
dfs2(1);
dfs3(1);
for(int i=1;i<=n;++i)
printf("%d%c",ans[i],i==n?'\n':' ');
}
return 0;
}
看到的一份正确的代码,有时间研究一下
#include <bits/stdc++.h>
#define F first
#define S second
#define pb push_back
#define mk make_pair
#define ll long long
#define dbg(x) printf(#x "=%d\n",x);
#define fastio ios_base::sync_with_stdio(0); cin.tie();
using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;
typedef vector< pii > vii;
typedef vector<ll> vl;
typedef vector< pll > vll;
const ll MOD=1e9+7;
const ll N=100010;
int n;
vi g[N];
int s,t,mxd;
int d[N],par[N];
bool mark[N];
queue<int> q;
void bfs(){
fill_n(d,n+1,0);
fill_n(par,n+1,0);
par[s]=s;
q.push(s);
int i,u;
while(!q.empty()){
u=q.front(); q.pop();
for(i=0;i<g[u].size();++i){
if(!par[g[u][i]]){
d[g[u][i]]=d[u]+1;
par[g[u][i]]=u;
q.push(g[u][i]);
}
}
}
t=u;
mxd=d[t];
}
int dp1[3][N],dp2[3][N];
void dfs(int v,int p,int dp[3][N]){
vi temp;
dp[0][v]=dp[1][v]=dp[2][v]=0;
for(int i=0;i<g[v].size();++i){
if(g[v][i]!=p){
dfs(g[v][i],v,dp);
dp[2][v]=max(dp[2][v],dp[1][g[v][i]]);
temp.pb(dp[0][g[v][i]]);
}
}
dp[1][v]=dp[2][v];
sort(temp.begin(),temp.end(),greater<int>());
if(temp.size()>0){
dp[0][v]=1+temp[0];
dp[1][v]=max(dp[1][v],dp[0][v]);
}
if(temp.size()>1){
dp[1][v]=max(dp[1][v],temp[0]+temp[1]+2);
}
}
int main(){
int test;
int i,j,k;
scanf("%d",&test);
while(test--){
scanf("%d",&n);
for(i=1;i<=n;++i){g[i].clear();
}
for(i=1;i<n;++i){
scanf("%d%d",&j,&k);
g[j].pb(k);
g[k].pb(j);
}
s=1;
bfs();
s=t;
bfs();
fill_n(mark,n+1,0);
for(i=t;par[i]!=i;i=par[i]){
mark[i]=1;
}
mark[s]=1;
dfs(s,s,dp1);
dfs(t,t,dp2);
for(i=1;i<=n;++i){
if(!mark[i]){
printf("%d ",mxd);
// printf("check\n");
// dbg(i);
}else{
printf("%d ",max(dp1[2][i],dp2[2][i]));
}
}
printf("\n");
}
return 0;
}
链接:https://www.codechef.com/problems/MXPATH
链接:https://codeforces.com/contest/911/problem/F
题意:给定一颗树,让你选择两个叶节点,累加它们的距离,然后删去其中一个点,连续操作n -1 次,问能够获得的最大累加距离是多少,输出选择和删除的方案。a、b、c,a和b表示选择的点,c表示a和b中删除的点
思路:根据求直径的思路,可以知道,与一个点在树上的最远距离,就是树上直径的两个端点中的一个。因此先删去直径外叶节点,然后删直径上的点。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e5+10,maxm=500+10;
int n;
vector<int> e[maxn],road;
vector<pair<int,int> > ans;
int d1,d2,depth[maxn],fa[maxn];
int diameter[maxn],dis[maxn][2];
ll sum;
int getdiameter(int s)
{
int d=0;
depth[s]=0;
queue<int> q;
q.push(s);
fa[s]=-1;
while(!q.empty())
{
int u=q.front();q.pop();
d=u;
for(auto v: e[u])
{
if(v==fa[u]) continue;
fa[v]=u;
depth[v]=depth[u]+1;
q.push(v);
}
}
return d;
}
void getroad()
{
int d=d2;
while(d!=-1)
{
road.push_back(d);
diameter[d]=1,d=fa[d];
}
}
void getdis(int u,int fa=-1,int t=0)
{
for(auto v: e[u])
{
if(v==fa) continue;
dis[v][t]=dis[u][t]+1;
getdis(v,u,t);
}
}
void getans(int u,int fa=-1)
{
for(auto v: e[u])
{
if(v==fa) continue;
if(diameter[v]) continue;
getans(v,u);
sum+=max(dis[v][0],dis[v][1]);
if(dis[v][0]>dis[v][1]) ans.push_back({d1,v});
else ans.push_back({d2,v});
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n-1;++i)
{
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
d1=getdiameter(1);
d2=getdiameter(d1);
getroad();
getdis(d1,-1,0);
getdis(d2,-1,1);
for(int i=1;i<=n;++i)
if(diameter[i]) getans(i);
ll k=(int)road.size()-1;
sum+=(1+k)*k/2;
for(int i=0;i<k;++i)
ans.push_back({d1,road[i]});
printf("%lld\n",sum);
for(auto x : ans)
{
cout<<x.first<<" "<<x.second<<" "<<x.second<<"\n";
}
return 0;
}
链接:https://nanti.jisuanke.com/t/41398
链接:https://codeforces.com/contest/1192/problem/B
参考链接: