IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> [CTSC2017]网络 -> 正文阅读

[数据结构与算法][CTSC2017]网络

网络

题解

首先需要我们观察出一个结论,我们先连接的边的两个端点必然都在原树的直径上。
我们将直径这条链当作原树的"根"来看看,即所有点的子树都是相对直径的子树。
如果我们连接的两个点在直径上同一个点的子树内,那么显然我们原来最长的直径长度必然不会减小,也就是说我们的最大值不变。
如果它连接的两个点在直径的不同子树中,这是原本直径两端点的距离虽然减小了,却没有减到最小。
我们考虑将条边的端点移动到子树的根,也就是直径上的节点上,直径两端点的距离会继续减小,增大的是这两子树内节点的距离,但这个距离增大后仍然不会超过直径现在的距离,否则它就是直径了。
所以必然存在一组最优解是两个点都在原树的直径上。

考虑在这个结论的基础上怎么求答案。
显然,对于最远距离距离最短这个问题,我们可以考虑二分求解,那么我们现在就相当于需要判断我们二分出的 m i d mid mid是否合法。
判断 m i d mid mid是否合法,不就是看我们当前的最大值是否小于 m i d mid mid嘛。
由于我们选择节点只会在直径上,所以每个直径上的点我们只需要记录下来它子树内最长链的长度。
我们记点 i i i内的最长链长度为 d i d_i di?,它在直径上的位置为 L i L_i Li?
对于直径上两个最长链距离大于 m i d mid mid的点,它们之间就只能走我们新连的边,我们记我们新连的边为 ( a , b ) ( a < b ) (a,b)(a<b) (a,b)(a<b),这里的 a , b a,b a,b大小是直径上的顺序,转化成数学表达式:
? i , j ( i < j ∧ L j ? L i + d i + d j > m i d ) , ∣ L i ? L a ∣ + ∣ L j ? L b ∣ + L + d i + d j ? m i d \forall i,j(i<j\wedge L_j-L_i+d_i+d_j>mid),|L_i-L_a|+|L_j-L_b|+L+d_i+d_j\leqslant mid ?i,j(i<jLj??Li?+di?+dj?>mid),Li??La?+Lj??Lb?+L+di?+dj??mid我们不妨将后面的绝对值拆开一下,相当于满足 4 4 4个式子。
L i + L j + L ? m i d + d i + d j ? L a + L b L i ? L j + L ? m i d + d i + d j ? L a ? L b ? L i + L j + L ? m i d + d i + d j ? ? L a + L b ? L i ? L j + L ? m i d + d i + d j ? ? L a ? L b L_i+L_j+L-mid+d_i+d_j\leqslant L_a+L_b\\ L_i-L_j+L-mid+d_i+d_j\leqslant L_a-L_b\\ -L_i+L_j+L-mid+d_i+d_j\leqslant -L_a+L_b\\ -L_i-L_j+L-mid+d_i+d_j\leqslant -L_a-L_b\\ Li?+Lj?+L?mid+di?+dj??La?+Lb?Li??Lj?+L?mid+di?+dj??La??Lb??Li?+Lj?+L?mid+di?+dj???La?+Lb??Li??Lj?+L?mid+di?+dj???La??Lb?我们可以用线段树维护出对于 i i i满足条件的 j j j,计算它们出每个 i i i,这四个式子左半边得到的最大值。
事实上我们需要在线段树上维护的就只有 d i + L i d_i+L_i di?+Li? d i ? L i d_i-L_i di??Li?的最大值。
我们得到了 L a + L b , L a ? L b , ? L a + L b , ? L a ? L b L_a+L_b,L_a-L_b,-L_a+L_b,-L_a-L_b La?+Lb?,La??Lb?,?La?+Lb?,?La??Lb?这四个式子的限制后,我们可以尝试枚举每个 a a a,看有没有能让满足条件的 b b b,如果有的话就说明对于我们这个 m i d mid mid有解。
这个排序后用指针就可以维护了,相当于看 4 4 4个集合有没有交集。

时间复杂度 O ( n log ? 2 n ) O\left(n\log^2n\right) O(nlog2n)

源码

也比较好写。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double Ld;
typedef pair<LL,LL> pii;
const LL INF=0x3f3f3f3f3f3f3f3f;
const int mo=1e9+7;
const int mod=1e6+7;
const int inv2=499122177;
const int inv3=332748118;
const int jzm=2333;
const int zero=2000;
const int n1=100;
const int orG=3,ivG=332748118;
const double Pi=acos(-1.0);
const double feps=1e-11;
const double eps=1e-9;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0)putchar('-'),print(-x);if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,L,head[MAXN],tot,pre[MAXN],father[MAXN],U,V;
int ord[MAXN],m,dep[MAXN],tmpa[MAXN],tmpb[MAXN],tota,totb;
LL f[MAXN],dis[MAXN],mx,b[MAXN];
bool key[MAXN];
struct edge{int to,nxt,paid;}e[MAXN<<1];
void addEdge(int u,int v,int w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
void dosaka(int u,int fa){
	f[u]=0;pre[u]=u;father[u]=fa;dep[u]=dep[fa]+1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to,w=e[i].paid;
		if(v==fa)continue;dosaka(v,u);
		if(f[u]+f[v]+w>mx)mx=f[u]+f[v]+w,U=pre[u],V=pre[v];
		if(f[v]+w>f[u])f[u]=f[v]+w,pre[u]=pre[v];
	}
}
void dosaka2(int u,int fa){
	f[u]=0;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==fa||key[v])continue;
		dosaka2(v,u);f[u]=max(f[u],f[v]+e[i].paid);
	}
}
class SegmentTree{
	private:
		LL tr1[MAXN<<2],tr2[MAXN<<2];
		void pushup(int rt){
			tr1[rt]=max(tr1[lson],tr1[rson]);
			tr2[rt]=max(tr2[lson],tr2[rson]);
		}
	public:
		void build(int rt,int l,int r){
			if(l==r){tr1[rt]=tr2[rt]=-INF;return ;}int mid=l+r>>1;
			build(lson,l,mid);build(rson,mid+1,r);pushup(rt); 
		}
		void insert(int rt,int l,int r,int ai,pii aw){
			if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;
			if(l==r){tr1[rt]=aw.fir;tr2[rt]=aw.sec;return ;}
			if(ai<=mid)insert(lson,l,mid,ai,aw);
			if(ai>mid)insert(rson,mid+1,r,ai,aw);
			pushup(rt);
		} 
		LL query1(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar)return -INF;
			if(al<=l&&r<=ar)return tr1[rt];
			int mid=l+r>>1;LL res=-INF;
			if(al<=mid)res=max(res,query1(lson,l,mid,al,ar));
			if(ar>mid)res=max(res,query1(rson,mid+1,r,al,ar));
			return res;
		}
		LL query2(int rt,int l,int r,int al,int ar){
			if(l>r||l>ar||r<al||al>ar)return -INF;
			if(al<=l&&r<=ar)return tr2[rt];
			int mid=l+r>>1;LL res=-INF;
			if(al<=mid)res=max(res,query2(lson,l,mid,al,ar));
			if(ar>mid)res=max(res,query2(rson,mid+1,r,al,ar));
			return res;
		}
}T;
bool check(LL mid){
	T.build(1,1,totb);LL tp1=-INF,tp2=-INF,tp3=-INF,tp4=-INF;
	for(int i=m;i>0;i--){
		int t=upper_bound(b+1,b+totb+1,mid+dis[i]-f[ord[i]])-b;
		LL tmp1=T.query1(1,1,totb,t,totb),tmp2=T.query2(1,1,totb,t,totb);
		tp1=max(tp1,f[ord[i]]+dis[i]+tmp1+L-mid);
		tp2=max(tp2,f[ord[i]]-dis[i]+tmp1+L-mid);
		tp3=max(tp3,f[ord[i]]+dis[i]+tmp2+L-mid);
		tp4=max(tp4,f[ord[i]]-dis[i]+tmp2+L-mid);
		t=lower_bound(b+1,b+totb+1,dis[i]+f[ord[i]])-b;
		T.insert(1,1,totb,t,mkpr(f[ord[i]]+dis[i],f[ord[i]]-dis[i]));
	}
	int r1=m+1,r2=1,l3=0,l4=m;
	for(int i=1;i<=m;i++){
		while(r1>1&&dis[r1-1]+dis[i]>=tp1)r1--;
		while(r2<=m&&dis[r2]-dis[i]<tp2)r2++;
		while(l3<m&&-dis[l3+1]+dis[i]>=tp3)l3++;
		while(l4>0&&-dis[l4]-dis[i]<tp4)l4--;
		if(min(l3,l4)>=max(r1,r2))return 1;
	}
	return 0;
}
int main(){
	while(scanf("%d %d",&n,&L)!=EOF&&n+L){
		for(int i=1;i<n;i++){
			int u,v,w;read(u);read(v);read(w);
			addEdge(u,v,w);addEdge(v,u,w);
		}
		dosaka(1,0);
		int nowu=U,nowv=V;
		if(dep[nowu]>dep[nowv])swap(nowu,nowv);
		while(dep[nowv]>dep[nowu])tmpb[++totb]=nowv,nowv=father[nowv];
		while(nowu^nowv)tmpa[++tota]=nowu,tmpb[++totb]=nowv,
			nowu=father[nowu],nowv=father[nowv];
		for(int i=1;i<=totb;i++)ord[++m]=tmpb[i];ord[++m]=nowu;
		for(int i=tota;i>0;i--)ord[++m]=tmpa[i];
		for(int i=1;i<=m;i++)key[ord[i]]=1;
		for(int i=1;i<=m;i++)dosaka2(ord[i],0);dis[1]=0;
		for(int i=1;i<m;i++)
			for(int j=head[ord[i]];j;j=e[j].nxt)
				if(e[j].to==ord[i+1])dis[i+1]=dis[i]+e[j].paid;
		totb=0;for(int i=1;i<=m;i++)b[++totb]=dis[i]+f[ord[i]];
		sort(b+1,b+totb+1);totb=unique(b+1,b+totb+1)-b-1;
		LL l=0,r=dis[m];
		while(l<r){LL mid=l+r>>1;if(check(mid))r=mid;else l=mid+1;}
		printf("%lld\n",l);
		for(int i=1;i<=n;i++)head[i]=key[i]=dis[i]=f[i]=pre[i]=dep[i]=0;
		mx=U=V=tot=tota=totb=m=0;
	}
	return 0;
}

谢谢!!!

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-07 22:56:50  更:2022-04-07 22:59:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:36:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码