大家都好强啊QAQ,这次题解大多看了lyd的书
P1099 && P2491 题解
Luogu树网的核 && Luogu消防
前排安利博客,LuitaryiJack && Chrisk && 喵の耳
或许会有更好的阅读体验
简化题意
emmmmmm,似乎没有办法简化题意(雾)
一个无环联通无向图,定义直径上一段长度不超过\({S}\)的路为\({F}\)(也可以是一个点),定义图中到\({F}\)的距离最长的为偏心距。求一个\({F}\),使得图上到\({F}\)的距离最大值最小,并输出这个距离
分析 是抄LYD+自己理解的
初步结论:
树的各个直径中点都交于一点
- 证明(@LuitaryiJack):如果两条直径没有交于一点,那么就代表你可以构造一条直径,长为原直径的长加上两个中点之间的距离,此时原直径就不符合直径的定义。所以树的各个直径中点都交于一点
任意一条直径上求出的最小偏心距都相等
证明:由结论\({1}\)可知,所有的直径都交于一点。所以其他的点到这些直径的距离就可以化为到中点的距离。又因为中点都相交于一点。所以我们可以知道任意一条直径上求出的偏心距都是相等的。
初步做法
O(\({N^{3}}\))
在树网的核这道题上,由于图的点数过少,所以我们可以用O(\({N^{3}}\))的方法水过这道题。即暴力枚举:先求出树的直径,再在上面枚举F,然后在\({F}\)上每一个点用\({dfs}\)求图上到\({F}\)的最远的点的距离,再取最小值即可
O(\({N^{2}}\))
然后考虑对枚举暴力进行优化。由于贪心可知,如果每次\({F}\)的选取越长,
通过感性理解我们求的的偏心距就越小。所以我们每次定\({F}\)长为\({S}\),然后枚举\({dfs}\)求偏心距
二级结论
这题满足单调的性质:偏心距越大,可以得到的点的位置越集中,使得由这些点构成的\({F}\)越短,即\({S}\)越小
证明:
QAQ感觉性质就是证明啊在二分\({mid}\)作为偏心距的大前提下,我们首先定义一些东西:- 直径两个端点为\({q}\)和\({v}\)
- 在直径上,离\({q}\)最长的不超过\({mid}\)的点为\({p}\),同理,\({u}\)是相对\({v}\)而言的那个点
- 根据直径的最长性,任何从\({p}\)和\({q}\)之间的直径中分叉离开的子树,其离\({p}\)最远的距离不会超过\({mid}\)。
所以\({p}\)和\({u}\)就是在偏心距不超过\({mid}\)的前提下,尽量靠近\({F}\)的点
二级做法 什么鬼标题
O(\({NlogSUM}\)),sum为树网中所有边的长度之和
二分\({mid}\),然后按照上面的结论证明找\({p}\)和\({u}\),然后判断他们之间的距离是否超过\({S}\),以及离\({F}\)最远的的点到这俩点的距离是否合法。若两者都满足,那么代表这是合法的
最终的做法 打这个题解好累啊QAQ
O(\({N}\))
设直径上的节点为\({u_{1},u_{2}.....u_{t}}\),那么先和前几种做法一样,把这些节点标记为已经访问,然后用深度优先遍历求\({d[u_{i}]}\),含义为从\({u_{i}}\)出发,不经过直径上的点,可以到达的最远距离。显然,偏心距要么就是由结论\({2}\)得到的直径两端点到\({F}\)的距离的较大值,要么就是直径外到\({F}\)的最大的距离。所以,首先枚举\({F}\)的两个端点取\({max}\)。最后由于最后要用\({F}\)完全遍历一遍直径,所以我们取\({max(d[u_{i}])}\)。然后扫一边直径即可求解
code
我选取了我们机房大佬们的\({code}\)给大家做参考。要是有侵权,emmmmm,那就侵权吧手动滑稽
1 O(\({N^{3}}\)) @喵の耳
#include
using namespace std;
namespace gengyf{
inline int read(){
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const int N=310;
int m[N][N],n,s,ans=inf;
int main(){
n=read();s=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
m[i][j]=inf;
if(i==j)m[i][j]=0;
}
for(int i=1;i=inf||m[i][j]<=len)continue;
len=m[i][j];
l=i;r=j;
}
for(int i=1;i<=n;i++){
if(m[l][i]+m[i][r]!=len)continue;
for(int j=1;j<=n;j++){
if(m[l][j]+m[r][j]!=len)continue;
if(m[i][j]>s)continue;
int ecc=-inf;
for(int k=1;k<=n;k++){
ecc=max(ecc,m[i][k]+m[j][k]-m[i][j]>>1);
}
ans=min(ans,ecc);
}
}
printf("%d",ans);
return 0;
}
}
int main(){
gengyf::main();
return 0;
}
markdown的代码插入不知道怎么折叠QAQ,好丑
2 O(\({N^{2}}\)) @LuitaryiJack
#include
#include
#include
#define R register int
using namespace std;
const int N=310;
inline int g() {
R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
}
struct edge {
int v,w,nxt;
#define v(i) e[i].v
#define w(i) e[i].w
#define nxt(i) e[i].nxt
}e[N<<1];
int n,m,k,st,ed,mx,ans=0x3f3f3f3f,tot,cnt,fir[N],pre[N],cnte[N],d[N],mem[N],sume[N];
inline void add(int u,int v,int w) {v(++tot)=v,w(tot)=w,nxt(tot)=fir[u],fir[u]=tot;}
bool vis[N];
void dfs(int u,int fa) {
//if(vis[u]) return ;
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(vis[v]||v==fa) continue;
d[v]=d[u]+w(i);
dfs(v,u);
pre[v]=u;
cnte[v]=i;
}
}
inline void solve() {
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d)); mx=0;
d[1]=0,dfs(1,0);
for(R i=1;i<=n;++i) if(d[i]>mx) mx=d[i],st=i;
memset(pre,0,sizeof(pre));
memset(d,0,sizeof(d)); mx=0;
d[st]=0,dfs(st,0);
for(R i=1;i<=n;++i) if(d[i]>mx) mx=d[i],ed=i;
for(R i=ed;i;i=pre[i]) mem[++cnt]=i,sume[cnt]=sume[cnt-1]+w(cnte[i]);
for(R i=1;i<=cnt;++i) { R j; mx=0;
memset(vis,false,sizeof(vis));
memset(d,0,sizeof(d));
for(j=i;j<=cnt;++j) if(sume[j-1]-sume[i-1]>k) break;
--j;
for(R t=i;t<=j;++t) vis[mem[t]]=true;
for(R t=i;t<=j;++t) dfs(mem[t],0);
for(R i=1;i<=n;++i) if(d[i]>mx) mx=d[i];
ans=min(mx,ans);
}
}
signed main() {
n=g(),k=g();
for(R i=1,u,v,w;i
3 O(\({NlogSUM}\)) @LuitaryiJack
#include
#include
#include
#define R register int
using namespace std;
int n,s,l=1,r=1;
int fa[300010],vis[300010],fir[300010],cnt;
long long d[300010],ans=0x3f3f3f3f;
inline int g() {
R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
}
struct node {
int v,w,nxt;
#define v(i) e[i].v
#define w(i) e[i].w
#define nxt(i) e[i].nxt
}e[600010];
inline void add(int u,int v,int w) {v(++cnt)=v,w(cnt)=w,nxt(cnt)=fir[u],fir[u]=cnt;}
inline void dfs(int u) {
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(vis[v]||fa[u]==v) continue;
fa[v]=u;
d[v]=d[u]+w(i);
dfs(v);
}
}
signed main() {
n=g(),s=g();
for(R i=1,u,v,w;id[l]?i:l;
memset(fa,0,sizeof(fa));
d[l]=0,dfs(l);
for(R i=1;i<=n;++i) r=d[i]>d[r]?i:r;
R t=r;
for(R i=r;i;i=fa[i]) {
while(fa[t]&&d[i]-d[fa[t]]<=s) t=fa[t];
ans=min((long long)ans,max(d[t],d[r]-d[i]));
}
for(R i=r;i;i=fa[i]) vis[i]=true;
for(R i=r;i;i=fa[i]) d[i]=0,dfs(i);
for(R i=1;i<=n;++i) if(!vis[i]) ans=max(ans,d[i]);
printf("%lld\n",ans);
}
4 O(\({N}\)) @我自己又丑又长又慢的代码QAQ
#include
#include
#include
using namespace std;
const int maxn=500010;
int head[maxn],ver[maxn<<1],nxt[maxn<<1],tot;
int edge[maxn<<1],fa[maxn],dp[maxn],val[maxn];
int dep[maxn],n,m,x,y,z,tmp,ans,s;bool v[maxn];
inline int read(){
char c=getchar();int x=0,f=1;
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f*x;
}
inline void add(int x,int y,int z){
ver[++tot]=y,nxt[tot]=head[x];
head[x]=tot;edge[tot]=z;
}
inline void dfs(int x){
for(register int i=head[x];i;i=nxt[i]){
if(ver[i]!=fa[x]){
dep[ver[i]]=dep[x]+edge[i];
fa[ver[i]]=x,dfs(ver[i]);
}
}
}
inline void diameter(){
dfs(1);x=1;
for(register int i=2;i<=n;i++)
if(dep[i]>dep[x]) x=i;//离根最远的点
dep[x]=0;memset(fa,0,sizeof(fa));
//换了根,所以fa会变
dfs(x);y=1;//两遍dfs求树的直径
for(register int i=2;i<=n;i++)
if(dep[i]>dep[y]) y=i;//离x最远的点
while(y!=x) v[y]=1,val[++m]=y,y=fa[y];
//把直径上x,y之间的点全部标记上访问过
//单开val[]记一下直径上都有什么
v[x]=1;val[++m]=x;
}
inline void treedp(int x){v[x]=1;
for(register int i=head[x];i;i=nxt[i])
if(!v[ver[i]]) treedp(ver[i]),
dp[x]=max(dp[x],dp[ver[i]]+edge[i]);
//在直径上dp,
//dp[]->从[]出发,不过直径上的点,可以到达的最远的点的距离
}
int main(){
n=read(),s=read();ans=0x3f3f3f3f;
for(register int i=1;i=1;i--){
while(j>=1&&dep[val[j]]-dep[val[i]]<=s) j--;
ans=min(ans,max(tmp,
max(dep[val[i]],dep[val[1]]-dep[val[j+1]])));
}
printf("%d",ans);
}
好像和chrisk没有关系啊,QAQ,算了,前排安利博客就安利了吧,反正也没有收钱都是同学QAQ
最后国际惯例,thanks for your attention
总算写完了,累死了