2018.11.05【NOIP2015】【洛谷2680】【UOJ#150】运输计划(二分答案+DFS序+树上差分)或(复杂度并不对(也不能过)的树链剖分)

24 篇文章 0 订阅
22 篇文章 0 订阅

洛谷传送门

UOJ传送门


解析:

UOJ上的数据很强,复杂度不对过不了的,但是 L C A LCA LCA如果是用倍增求的话也过不了(已经加了上界优化)。。。毕竟树剖常数小,复杂度还不满。。。

思路:

首先,不要试图化边为点,每条边的信息可以存在它所指向的儿子中。

解法1:UOJ上不能过的树链剖分

其实我们只需要考虑断掉哪些边就行了,显然断掉最长路径上以外的边是没有作用的,所以我们只需要考虑枚举断掉最长路径上的边就行了。

而经过这条边的其他路径其实也会减小,不可能成为新的最长路径,所以我们还需要记录没有经过这条边的最长路径,然后用 原最长路径减去断掉的边 和没有经过这条边的最长路径中较大的来更新答案。

维护最大值可以使用线段树+DFS序来做,每次处理出没有被该路径覆盖的部分,然后线段树区间更新最大值就行了。

最后直接在最长链上面跑一边就好了。
复杂度瓶颈在于更新最大值 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)

解法2:复杂度十分优秀的二分答案+树上差分

首先,这个显然的二分应该不需要解释,解的存在性满足单调性质。

那么怎么 c h e c k check check

考虑所有长度大于当前二分答案的路径都需要断边,那么我们直接树上差分按照 D F S DFS DFS序倒着跑一遍就可以知道哪些边是被所有不满足当前答案的覆盖的,然后依次断开,看最大值是否小于二分的答案就行了。

这样做显然是正确的,因为我们所有的二分答案只考虑当前不满足答案的需要变小的路径,而这些路径全部变小后显然只需要考虑它们当中最后的最大值是否能够满足要求,不然就肯定 g g gg gg


代码(树链剖分):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=300005;
int last[N],nxt[N<<1],to[N<<1],ecnt;
int w[N<<1];
inline void addedge(int u,int v,int val){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,w[ecnt]=val;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,w[ecnt]=val;
}

int n,m;
int fa[N],siz[N],top[N],son[N],dep[N],dist[N],pre[N];
inline void dfs1(int u){
	siz[u]=1;
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa[u])continue;
		dep[v]=dep[u]+1;
		dist[v]=dist[u]+(pre[v]=w[e]);
		fa[v]=u;
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}

int in[N],pos[N],tot;
inline void dfs2(int u){
	pos[in[u]=++tot]=u;
	if(son[u]){
		top[son[u]]=top[u];
		dfs2(son[u]);
	}
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==son[u]||v==fa[u])continue;
		top[v]=v;
		dfs2(v);
	}
}

inline void tree_dissection(int root=1){
	dfs1(root);
	top[root]=root;
	dfs2(root);
}

inline int LCA(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]>dep[top[v]])swap(u,v);
		v=fa[top[v]];
	}
	return dep[u]>dep[v]?v:u;
}

int maxn[N<<2],tag[N<<2];
inline void pushup(int k){maxn[k]=max(maxn[k<<1],maxn[k<<1|1]);}
inline void pushnow(int k,int val){
	maxn[k]=max(maxn[k],val);
	tag[k]=max(tag[k],val);
}
inline void pushdown(int k){
	if(tag[k]){
		pushnow(k<<1,tag[k]);
		pushnow(k<<1|1,tag[k]);
		tag[k]=0;
	}
}

inline void update(int k,int l,int r,cs int &ql,cs int &qr,cs int &val){
	if(ql<=l&&r<=qr)return pushnow(k,val);
	int mid=(l+r)>>1;pushdown(k);
	if(ql<=mid)update(k<<1,l,mid,ql,qr,val);
	if(qr>mid)update(k<<1|1,mid+1,r,ql,qr,val);
	pushup(k);
}

inline int query(int k,int l,int r,cs int &pos){
	if(l==r)return maxn[k];
	int mid=(l+r)>>1;pushdown(k);
	if(pos<=mid)return query(k<<1,l,mid,pos);
	else return query(k<<1|1,mid+1,r,pos);
}

inline void update(int u,int v,int val){
	vector<pair<int,int> > a;
	while(top[u]!=top[v]){
		if(dep[top[u]]>dep[top[v]])swap(u,v);
		a.push_back(make_pair(in[top[v]],in[v]));
		v=fa[top[v]];
	}
	if(dep[u]>dep[v])swap(u,v);
	a.push_back(make_pair(in[u]+1,in[v]));
	sort(a.begin(),a.end());
	if(a[0].first>1)update(1,1,n,1,a[0].first-1,val);
	if(a.back().second<n)update(1,1,n,a.back().second+1,n,val);
	for(int re i=1;i<a.size();++i)update(1,1,n,a[i-1].second+1,a[i].first,val);
}

int mxdis,tu,tv;

inline int getans(int u,int v){
	int ans=mxdis;
	if(u==v)return 0;
	if(dep[u]>dep[v])swap(u,v);
	while(u^v){
		if(dep[u]>dep[v]){
			ans=min(ans,max(mxdis-pre[u],query(1,1,n,in[u])));
			u=fa[u];
		}
		else {
			ans=min(ans,max(mxdis-pre[v],query(1,1,n,in[v])));
			v=fa[v];
		}
	}
	return ans;
}

signed main(){
	n=getint();
	m=getint();
	for(int re i=1;i<n;++i){
		int u=getint(),v=getint(),val=getint();
		addedge(u,v,val);
	}
	tree_dissection();
	for(int re i=1;i<=m;++i){
		int u=getint(),v=getint();
		int lca=LCA(u,v);
		int dis=dist[u]+dist[v]-2*dist[lca];
		if(dis>=mxdis){
			mxdis=dis;
			tu=u,tv=v;
		}
		update(u,v,dis);
	}
	cout<<getans(tu,tv);
	return 0;
}

代码(二分答案+树上差分):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
	return num;
}

cs int N=300005;
int last[N],nxt[N<<1],to[N<<1],ecnt;
int W[N<<1];
inline void addedge(int u,int v,int val){
	nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,W[ecnt]=val;
	nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,W[ecnt]=val;
}

int fa[N],dep[N],top[N],siz[N],son[N],dist[N],pre[N],in[N],pos[N],tot;
inline void dfs1(int u){
	siz[u]=1;
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa[u])continue;
		dep[v]=dep[u]+1;
		fa[v]=u;
		dist[v]=dist[u]+(pre[v]=W[e]);
		dfs1(v);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}

inline void dfs2(int u){
	pos[in[u]=++tot]=u;
	if(son[u]){
		top[son[u]]=top[u];
		dfs2(son[u]);
	}
	for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
		if(v==fa[u]||v==son[u])continue;
		top[v]=v;
		dfs2(v);
	}
}

inline void tree_dissection(int root=1){
	dfs1(root);
	top[root]=root;
	dfs2(root);
}

inline int LCA(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]>dep[top[v]])swap(u,v);
		v=fa[top[v]];
	}
	return dep[u]>dep[v]?v:u;
}

int n,m;
int x[N],y[N],lca[N],w[N],mxdis;
int cnt[N],con;
inline bool check(int lim){
	memset(cnt,0,sizeof cnt);
	con=0;
	for(int re i=1;i<=m;++i){
		if(w[i]>lim){
			++cnt[x[i]];
			++cnt[y[i]];
			cnt[lca[i]]-=2;
			++con;
		}
	}
	for(int re i=n;i;--i){
		cnt[fa[pos[i]]]+=cnt[pos[i]];
		if(cnt[pos[i]]>=con&&mxdis-pre[pos[i]]<=lim)return true;
	}
	return false;
}

int l,r;
signed main(){
	n=getint();
	m=getint();
	for(int re i=1;i<n;++i){
		int u=getint(),v=getint(),val=getint();
		addedge(u,v,val);
		l=max(l,val);
	}
	tree_dissection();
	for(int re i=1;i<=m;++i){
		x[i]=getint();
		y[i]=getint();
		lca[i]=LCA(x[i],y[i]);
		w[i]=dist[x[i]]+dist[y[i]]-dist[lca[i]]*2;
		r=max(r,w[i]);
	}
	mxdis=r;
	l=r-l-1;
	while(l<r){
		int mid=(l+r)>>1;
		if(check(mid))r=mid;
		else l=mid+1;
	}
	cout<<l;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值