首先贴出一篇我认为讲的最好的:
http://blog.csdn.net/vmurder/article/details/44627469
首先证明结论:
证明一:树的核必在直径上
1.选定的核与直径无交集
显然选的核在直径的一个分支上,如图,肯定不如核与直径相接的那段直径优
2.选定的核与直径有一部分交集
如图若选红色部分为核,那么不在直径的一部分相当于优化了BC段的长度,然而如果AD为直径是条件,那么BC必然不比BD长,那么相当于最远的距离没有改变,肯定不如全在直径上优
3.排除以上两种情况,确定选定的核应当在直径上
证明二:任意一条直径均可,不必特定直径
设选定的核d在直径L1上,另有一条直径L2,易证L1与L2一定有交集(反证一下就就好)
1.d与L2无交集
易看出红色部分很明显比绿色部分劣
2.d与L2有部分交集
红色部分优化掉了DE,而由于如图所示两条直线均为直径,所以由反证法有DE=DF,于是实际上优化掉DE,而DF依旧没有改变,是无效的,不如全部放在CD上优(为啥不放在EF上呢。。。如证明一所示)
3.排除掉两种情况,树的核必然优先在直径相交处
对于这道题应该怎么找直径相交呢,枚举?肯定不现实,而且还得讨论,于是在一条直径上找,必然包含直径相交的情况,然后观察在直径上的性质,发现对于一段核,发生贡献的总共就两个地方:一个是核两端点到直径两端点的距离,另一个是核上伸出的分枝到核的距离
然后固定一个右端点,肯定选择核的部分越大越优,于是左端点应该尽力向左,易知随着右端点向右,左端点的位置肯定单调向前(意会一下不用较真),第一步贡献好求,第二步贡献则须考虑到记录伸出分枝的最长深度。每次左端分枝数减小,右端加一。根据单调队列的性质,需要满足两个单调,一个是位置单调,一个是值单调。对于两个同时在队列的值v1比v2先进队且v1 < v2,则v1比v2先出队,有v2保证,v1删去不会造成影响,这是单调队列的定义。而对于这道题每次右节点右移,左节点相应变化后维护一个分枝深度的单调递减值,而进队时间单调递增的队列即可,每回取队首即为最大值。
求树的直径用dfs可能会爆栈,不如用bfs好了。。。
#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn=500005;
struct edge
{
int to,next,val;
}e[maxn<<1];
int n,cnt,xx,yy,length,S,ans=0x3f3f3f3f;
int head[maxn],depth[maxn],len[maxn],pere[maxn],val[maxn],sum[maxn],pos[maxn];
bool vst[maxn];
void insert(int a,int b,int c)
{
e[++cnt].to=b;e[cnt].val=c;e[cnt].next=head[a];head[a]=cnt;
}
void bfs(int s,int &o)
{
memset(depth,0,sizeof depth);
memset(pere,0,sizeof pere);
memset(val,0,sizeof val);
queue<int>q;
q.push(s);
o=s;depth[s]=1;
pere[s]=-1;
while(!q.empty())
{
int x=q.front();
q.pop();
for(int i=head[x];i;i=e[i].next)if(e[i].to!=pere[x])
{
int y=e[i].to;
pere[y]=x;
depth[y]=depth[x]+e[i].val;
val[y]=e[i].val;
if(depth[y]>depth[o])o=y;
q.push(y);
}
}
}
void diameter()
{
bfs(1,xx);
bfs(xx,yy);
}
void dfs(int x,int &key)
{
vst[x]=true;
for(int i=head[x];i;i=e[i].next)if(!vst[e[i].to]&&e[i].to!=pere[x])
{
depth[e[i].to]=depth[x]+e[i].val;
if(depth[e[i].to]>key)key=depth[e[i].to];
dfs(e[i].to,key);
}
}
void traversal()
{
length=0;
memset(depth,0,sizeof depth);
for(int i=yy;i!=-1;i=pere[i])
pos[++length]=i,
sum[length]=sum[length-1]+val[pos[length-1]],
dfs(i,len[length]);
}
int q[maxn];
void solve()
{
int head=1,rear=0,left=1;//
for(int i=1;i<=length;i++)
{
while(head<=rear&&sum[i]-sum[left]>S)left++;
while(head<=rear&&left>head)head++;
while(head<=rear&&len[q[rear]]<=len[i])rear--;
q[++rear]=i;
ans=min(ans,max(len[q[head]],max(sum[left],sum[length]-sum[i])));
}
cout<int main()
{
scanf("%d%d",&n,&S);
for(int i=1,u,v,val;i"%d%d%d",&u,&v,&val),
insert(u,v,val),
insert(v,u,val);
diameter();
traversal();
solve();
return 0;
}