传送门
如果题目给的是一棵树,那么答案显然应该是树的直径/2。
不过题目给的是一个环套树,那么如果我们暴力地删去环上的一条边然后求新生成的树的最长链,然后取min的话,当然是正确的,不过时间复杂度是 O(n2) 的难以承受。
那么我们可以这样考虑:因为要使最长链最短,那么我们的任务就是求出一种树的形态,使这种情况下的最长链在所有情况中最短。
首先暴力找环是必须的,时间复杂度 O(n) 。
然后分两种情况考虑:
①如果最长链不经过环,即最长链在外向树上,那么枚举环上的每一个点,然后在每一棵外向树上进行dp,就能求出外向树上的最长链。时间复杂度 O(n)
②如果最长链经过环,那么很显然它不会经过环上的某一条边(类似删边的思想)。假设s[i]表示以i为根的外向树从树根到叶节点的最长距离,sum[i]表示距离的前缀和(当前删掉的边不管,其余的变成一个序列)的话,那么最长链应该为 max{sum[i]−sum[j]+s[i]+s[j]}=max{(sum[i]+s[i])−(sum[j]+s[j])}=max{sum[i]+s[i]}−min{sum[j]+s[j]} 。可以看出可以用两棵线段树来维护,不过因为i不能与j相等,所以还需要维护次大值和次小值。在枚举的过程中修改一下线段树中的值就好了。时间复杂度 O(nlogn) 。
值得注意的是第一种情况应该将所有的最长链取max,即无论如何删边答案都不会小于外向树上的最长链。但是第二种情况应该取min,以为要通过删边得到一组最优解。最后再将两种情况的答案取max。
#include
#include
#include
#include
#include
using namespace std;
#define N 100005
#define LL long long
int n,x,y,cnt,last;
LL z,Max1,Max2,ans1,ans2,ans,t,lastsum;
int tot,point[N],nxt[N*2],v[N*2]; LL c[N*2];
int father[N],o[N];
LL len[N],length[N],f[N],g[N],s[N],sum[N],delta[N*4];
bool vis[N],flag;
struct hp{LL val;int num;}maxn[N*4],_maxn[N*4],minn[N*4],_minn[N*4],a[5];
inline int read()
{
char ch=getchar(); int x=0;
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
inline void addedge(int x,int y,LL z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
inline void Circle(int x,int pt)
{
while (x!=pt)
{
o[++cnt]=x;
x=father[x];
}
o[++cnt]=pt;
}
inline void dfs(int x,int fa)
{
vis[x]=true; father[x]=fa;
for (int i=point[x];i&&(!flag);i=nxt[i])
if (v[i]!=fa)
{
len[v[i]]=c[i];
if (vis[v[i]])
{
Circle(x,v[i]);
flag=true;
break;
}
dfs(v[i],x);
}
}
inline void treedp(int x,int fa)
{
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa&&!vis[v[i]])
{
treedp(v[i],x);
if (f[v[i]]+c[i]>f[x])
{
g[x]=f[x];
f[x]=f[v[i]]+c[i];
}
else g[x]=max(g[x],f[v[i]]+c[i]);
}
Max1=max(Max1,f[x]+g[x]);
Max2=max(Max2,f[x]);
}
inline int cmpmax(hp a,hp b)
{
return a.val>b.val;
}
inline int cmpmin(hp a,hp b)
{
return a.valint now)
{
a[1].val=maxn[now<<1].val,a[1].num=maxn[now<<1].num;
a[2].val=maxn[now<<1|1].val,a[2].num=maxn[now<<1|1].num;
a[3].val=_maxn[now<<1].val,a[3].num=_maxn[now<<1].num;
a[4].val=_maxn[now<<1|1].val,a[4].num=_maxn[now<<1|1].num;
sort(a+1,a+5,cmpmax);
maxn[now].val=a[1].val,maxn[now].num=a[1].num;
for (int i=2;i<=4;++i)
if (a[i].num!=maxn[now].num)
{
_maxn[now].val=a[i].val,_maxn[now].num=a[i].num;
break;
}
a[1].val=minn[now<<1].val,a[1].num=minn[now<<1].num;
a[2].val=minn[now<<1|1].val,a[2].num=minn[now<<1|1].num;
a[3].val=_minn[now<<1].val,a[3].num=_minn[now<<1].num;
a[4].val=_minn[now<<1|1].val,a[4].num=_minn[now<<1|1].num;
sort(a+1,a+5,cmpmin);
minn[now].val=a[1].val,minn[now].num=a[1].num;
for (int i=2;i<=4;++i)
if (a[i].num!=minn[now].num)
{
_minn[now].val=a[i].val,_minn[now].num=a[i].num;
break;
}
}
inline void build(int now,int l,int r)
{
int mid=(l+r)>>1;
if (l==r)
{
maxn[now].val=_maxn[now].val=sum[l]+s[l];
minn[now].val=_minn[now].val=sum[l]-s[l];
maxn[now].num=_maxn[now].num=minn[now].num=_minn[now].num=l;
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
inline void pushdown(int now,int l,int r,int mid)
{
if (delta[now])
{
maxn[now<<1].val+=delta[now]; _maxn[now<<1].val+=delta[now];
minn[now<<1].val+=delta[now]; _minn[now<<1].val+=delta[now];
maxn[now<<1|1].val+=delta[now]; _maxn[now<<1|1].val+=delta[now];
minn[now<<1|1].val+=delta[now]; _minn[now<<1|1].val+=delta[now];
delta[now<<1]+=delta[now]; delta[now<<1|1]+=delta[now];
delta[now]=0;
}
}
inline void interval_change(int now,int l,int r,int lrange,int rrange,LL v)
{
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange)
{
maxn[now].val+=v; _maxn[now].val+=v;
minn[now].val+=v; _minn[now].val+=v;
delta[now]+=v;
return;
}
pushdown(now,l,r,mid);
if (lrange<=mid) interval_change(now<<1,l,mid,lrange,rrange,v);
if (mid+1<=rrange) interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
update(now);
}
inline LL query()
{
if (maxn[1].num!=minn[1].num) return maxn[1].val-minn[1].val;
else return max(maxn[1].val-_minn[1].val,_maxn[1].val-minn[1].val);
}
int main()
{
n=read();
for (int i=1;i<=n;++i)
{
x=read(); y=read(); z=read();
addedge(x,y,(LL)z);
}
dfs(1,0);
for (int i=1;i<=cnt;++i) length[i]=len[o[i]];
memset(vis,0,sizeof(vis));
for (int i=1;i<=cnt;++i) vis[o[i]]=true;
for (int i=1;i<=cnt;++i)
{
Max1=Max2=0;
treedp(o[i],0);
ans1=max(ans1,Max1);
s[i]=Max2;
}
for (int i=1;i1]=sum[i]+length[i];
build(1,1,cnt);
t=query();
ans2=t;
lastsum=sum[cnt];
last=cnt;
for (int i=1;i1,1,cnt,1,cnt,-length[i]);
interval_change(1,1,cnt,i,i,lastsum+length[last]);
t=query();
ans2=min(ans2,t);
lastsum=lastsum-length[i]+length[last];
last=i;
}
ans=max(ans1,ans2);
double loc=(double)ans/2;
printf("%0.1lf\n",loc);
}
①memset非常慢,时间应该是 O(n) 的,能不用就不用。