第一次写这种大模拟题呢。。。觉得很考验码力和阅读理解能力,就写上了。
题意简述
给定一个带权的树,
定义:
- 点 x x x到路径 P P P的距离: P P P中离 x x x最远的点的到 x x x的距离
- 一条路径 P P P的偏心距为:树上离路径 P P P最远的点到 P P P的距离
请找到一个路径 P P P,使得:
- P P P的所有点在这个树的直径上
- P P P中的边权和 < = S <=S <=S, S S S给定
- P P P的偏心距最小
输出最小的偏心距。
思路
先说一下,这个题数据水甚,大概 O ( n 4 ) O(n^4) O(n4)也是能过去的,只要顾着写就可以了,几乎不用考虑时间复杂度。
首先找到直径。然后在直径上 O ( n 2 ) O(n^2) O(n2)枚举路径的两个端点,计算出这个路径的偏心距,更新答案即可。
几个细节:
- 偏心距就两遍 D F S DFS DFS更新一下
- 如果路径长度 > S >S >S,那么及时 b r e a k break break,不要继续搜了(一个大优化)
- 适当使用 S T L STL STL以减少代码量,增加准确率( S T L STL STL赛高!)
- 不要出现低级错误!!!(像我就写反了 i , j i,j i,j,还好我机智,要不然肝没了)
代码(与以前的题目不同,这个题目在于读代码)
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 11234
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define Tra(i,u) for(int i=G.Start(u);~i;i=G.Next(i))
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
class Graph//存图
{
public:
int head[N];
int EdgeCount;
struct Edge
{
int To,Label,Next;
}Ed[N<<3];
void clear()
{
memset(head,-1,sizeof(head));
memset(Ed,-1,sizeof(Ed));
EdgeCount=0;
}
void AddEdge(int u,int v,int w=1)
{
++EdgeCount;
Ed[EdgeCount]=(Edge){v,w,head[u]};
head[u]=EdgeCount;
}
void Add2(int u,int v,int w=1)
{
AddEdge(u,v,w);AddEdge(v,u,w);
}
int Start(int u)
{
return head[u];
}
int To(int u)
{
return Ed[u].To;
}
int Label(int u)
{
return Ed[u].Label;
}
int Next(int u)
{
return Ed[u].Next;
}
}G;
int n,s;
void R1(int &x)
{
x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=(f==1)?x:-x;
}
void Input()
{
G.clear();
R1(n),R1(s);
F(i,1,n-1)
{
int u,v,w;
R1(u),R1(v),R1(w);
G.Add2(u,v,w);
}
}
int dis[N],fa[N];
void DFS(int u,int f)//非常普通的DFS
{
if (u==f) dis[u]=0,fa[u]=-1;
else fa[u]=f;
Tra(i,u)
{
int v=G.To(i);
if (v!=f)
{
dis[v]=dis[u]+G.Label(i);
DFS(v,u);
}
}
}
int dis2[N];//注意:这个要新开一个dis数组用,因为上面那个dis数组要用来判断路径长度是否<=S
bool In[N];
void DFS2(int u,int f)
{
Tra(i,u)
{
int v=G.To(i);
if (!In[v] and v!=f)//注意:判断!In[v]是为了只考虑路径外面的点
{
dis2[v]=dis2[u]+G.Label(i);
DFS2(v,u);
}
}
}
void Soviet()
{
int l,r;//直径的两端
DFS(1,1);
l=max_element(dis+1,dis+n+1)-dis;//max_element:返回区间内最大值出现的第一个位置
// min_element同理,返回区间内最小值出现的第一个位置
DFS(l,l);
r=max_element(dis+1,dis+n+1)-dis;
// printf("diameter: %d %d\n",l,r);
// 直径的求法:从1开始找到最远的点,就是第一个端点。再从这个端点跑到最远的点,就是第二个端点。
int ans=0x3f3f3f3f;
for(int i=r;~i;i=fa[i])
{
for(int j=i;~j;j=fa[j])//枚举路径上的两个点
{
if (dis[i]-dis[j]<=s)//注意,此处的dis是以r为根的意义下算的,但是fa数组是以
// l为根的意义下算的,所以j是在i的下面的,而不是上面,所以我这里i和j写反了,大家千万不要再犯这个错误了T_T
{
// printf("i=%d j=%d\n",i,j);
FK(In);
for(int k=i;k!=j;k=fa[k])
{
In[k]=1;
}
In[j]=1;//标记路径上的点
for(int k=i;k!=j;k=fa[k])
{
dis2[k]=0;
DFS2(k,k);
}
dis2[j]=0;DFS2(j,j);//更新最小距离
ans=min(ans,*max_element(dis2+1,dis2+n+1));
//更新答案
}
else break;//注意及时break
}
}
printf("%d\n",ans);
}
#define FlandreScarlet void
FlandreScarlet IsMyWife()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
Input();
Soviet();
}
};
int main()
{
Flandle_Scarlet::IsMyWife();
return 0;
}
博客详细介绍了洛谷第1099题的解题思路,这是一道大模拟题,重点在于寻找树的直径并在直径上找到满足特定条件的路径。题意包括计算点到路径的距离和路径的偏心距。解题方法是首先找到树的直径,然后枚举路径的两个端点,计算并更新最小偏心距。博客提到了数据规模较小,允许使用较简单的O(n^2)算法,并强调了几个编程细节,如使用DFS更新偏心距、避免超限、利用STL减少代码错误。
483

被折叠的 条评论
为什么被折叠?



