首页 > 其他分享 >[WC2018] 通道题解

[WC2018] 通道题解

时间:2023-12-27 10:48:37浏览次数:27  
标签:lc int 题解 tot WC2018 dep cun id 通道

先考虑只有两颗树要咋做,柿子先变成 \(dep_x+dep_y-2\times dep_{lca}+dist_2(x,y)\)
我们可以新建节点 \(x'\rightarrow x\),边权为 \(dep_x\),这样上面的式子可以看作枚举 \(lca\) 后,选出一个端点在不同子树中的直径,可以直接 \(DP\) 合并直径

再考虑多了一颗树,我们可以进行边分治,枚举断边之后,柿子变成 \(d_x+d_y+val+dep_{2x}+dep_{2y}-2\times dep_{2lca}+dist_{3}(x,y)\)
同理有了这个式子之后我们可以在虚树上 \(DP\) 合并直径,端点必须为不同子树的且颜色不同的点

code

#include<bits/stdc++.h>
using namespace std;
#define N 400005
#define int long long
int top,n,ans=-1e18;
int q[N],val[N],col[N];
namespace TREE{
	int k,tot;
	int h[N],id[N],lc[N][20],dep[N],lg[N],deep[N];
	vector<int> G[N];
	struct AB{
		int a,b,c,n;
	}d[N*2];
	void cun(int x,int y,int z){
		d[++k]={x,y,z,h[x]},h[x]=k;
	}
	void dfs(int x,int fa){
		lc[++tot][0]=x,id[x]=tot,deep[x]=deep[fa]+1;
		for(int i=h[x];i;i=d[i].n){
			int y=d[i].b;
			if(y==fa) continue;
			dep[y]=dep[x]+d[i].c;
			dfs(y,x);lc[++tot][0]=x;
		}
	}
	int mn(int x,int y){
		return deep[x]<deep[y]?x:y;
	}
	int Lca(int x,int y){
		if(id[x]>id[y]) swap(x,y);
		int p=lg[id[y]-id[x]+1];
		return mn(lc[id[x]][p],lc[id[y]-(1<<p)+1][p]);
	}
	int dis(int x,int y){
		if(!x||!y) return -1e18;
		int lca=Lca(x,y);
		return dep[x]+dep[y]-2*dep[lca];
	}
	void init(){
		for(int i=1,x,y,z;i<n;i++){
			scanf("%lld%lld%lld",&x,&y,&z);
			cun(x,y,z),cun(y,x,z);
		}
		dfs(1,0);
		for(int i=2;i<=tot;i++) lg[i]=lg[i/2]+1;
		for(int j=1;j<=19;j++){
			for(int i=1;i+(1<<j)-1<=tot;i++) lc[i][j]=mn(lc[i][j-1],lc[i+(1<<(j-1))][j-1]);
		}
	}
}
struct ZJ{
	int a,b,len;
};
int merge(ZJ x,ZJ y){
	int ans=-1e18;
	ans=max(ans,val[x.a]+val[y.a]+TREE::dis(x.a,y.a));
	ans=max(ans,val[x.a]+val[y.b]+TREE::dis(x.a,y.b));
	ans=max(ans,val[x.b]+val[y.a]+TREE::dis(x.b,y.a));
	ans=max(ans,val[x.b]+val[y.b]+TREE::dis(x.b,y.b));
	return ans;
}
ZJ mk_zj(int x,int y){
	ZJ z={x,y,val[x]+val[y]+TREE::dis(x,y)};
	return z;
}
ZJ mx(ZJ x,ZJ y){
	return x.len>y.len?x:y;
}
ZJ bing(ZJ x,ZJ y){
	ZJ z=mx(x,y);
	z=mx(z,mk_zj(x.a,y.a));z=mx(z,mk_zj(x.a,y.b));z=mx(z,mk_zj(x.b,y.a));z=mx(z,mk_zj(x.b,y.b));
	return z;
}
namespace XS{
	int k,res,t,tot;
	int h[N],sta[N],id[N],lc[N][20],dep[N],lg[N],vis[N],deep[N];
	ZJ f[N][2];
	vector<int> G[N];
	struct AB{
		int a,b,c,n;
	}d[N*2];
	void cun(int x,int y,int z){
		d[++k]={x,y,z,h[x]},h[x]=k;
	}
	void dfs(int x,int fa){
		lc[++tot][0]=x,id[x]=tot,deep[x]=deep[fa]+1;
		for(int i=h[x];i;i=d[i].n){
			int y=d[i].b;
			if(y==fa) continue;
			dep[y]=dep[x]+d[i].c;
			dfs(y,x);lc[++tot][0]=x;
		}
	}
	int mn(int x,int y){
		return deep[x]<deep[y]?x:y;
	}
	void init(){
		for(int i=1,x,y,z;i<n;i++){
			scanf("%lld%lld%lld",&x,&y,&z);
			cun(x,y,z),cun(y,x,z);
		}
		dfs(1,0);
		for(int i=2;i<=tot;i++) lg[i]=lg[i/2]+1;
		for(int j=1;j<=19;j++){
			for(int i=1;i+(1<<j)-1<=tot;i++) lc[i][j]=mn(lc[i][j-1],lc[i+(1<<(j-1))][j-1]);
		}
	}
	int Lca(int x,int y){
		if(id[x]>id[y]) swap(x,y);
		int p=lg[id[y]-id[x]+1];
		return mn(lc[id[x]][p],lc[id[y]-(1<<p)+1][p]);
	}
	void ins(int x){
		if(!vis[x]) G[x].clear(),vis[x]=1;
		if(!t){
			sta[++t]=x;
			return;
		}
		int lca=Lca(x,sta[t]);
		if(!vis[lca]) G[lca].clear(),col[lca]=-1,vis[lca]=1;
		if(lca!=sta[t]){
			while(t>1&&id[lca]<id[sta[t-1]]) G[sta[t-1]].push_back(sta[t]),t--;
			if(id[lca]!=id[sta[t-1]]) G[lca].push_back(sta[t]),sta[t]=lca;
			else G[lca].push_back(sta[t]),t--;
		}
		sta[++t]=x;
	}
	void DP(int x){
		f[x][0]=f[x][1]={0,0,(int)-1e18};
		if(col[x]!=-1) f[x][col[x]]={x,x,val[x]};
		for(auto y:G[x]){
			DP(y);
			res=max(res,max(merge(f[x][0],f[y][1]),merge(f[x][1],f[y][0]))-2*dep[x]);
			f[x][0]=bing(f[x][0],f[y][0]),f[x][1]=bing(f[x][1],f[y][1]);
		}
		vis[x]=0;
	}
	bool cmp(int x,int y){
		return id[x]<id[y];
	}
	int work(){
		res=-1e18,t=0;
		sort(q+1,q+1+top,cmp);
		if(q[1]!=1) G[1].clear(),col[1]=-1,vis[1]=1,sta[++t]=1;
		for(int i=1;i<=top;i++) ins(q[i]);
		while(t>1) G[sta[t-1]].push_back(sta[t]),t--;
		DP(1);
		return res;
	}
}
namespace BFZ{
	int k=1,ssz,rt,tot;
	int h[N],dep[N],sz[N],vis[N];
	vector<pair<int,int> > G[N];
	struct AB{
		int a,b,c,n;
	}d[N*2];
	void cun(int x,int y,int z){
		d[++k]={x,y,z,h[x]},h[x]=k;
	}
	void rebuild(int x,int fa){
		int tmp=0,lst=0;
		for(auto p:G[x]){
			int y=p.first,z=p.second;
			if(y==fa) continue;
			tmp++;
			if(tmp==1) cun(x,y,z),cun(y,x,z),lst=x;
			else if(tmp==(int)G[x].size()-(x!=1)) cun(lst,y,z),cun(y,lst,z);
			else tot++,cun(lst,tot,0),cun(tot,lst,0),cun(tot,y,z),cun(y,tot,z),lst=tot;
		}
		for(auto p:G[x]){
			int y=p.first;
			if(y==fa) continue;
			rebuild(y,x);
		}
	}
	void init(){
		for(int i=1,x,y,z;i<n;i++){
			scanf("%lld%lld%lld",&x,&y,&z);
			G[x].push_back({y,z}),G[y].push_back({x,z});
		}tot=n;
		rebuild(1,0);
	}
	void gt_rt(int x,int fa,int siz){
		sz[x]=1;
		for(int i=h[x];i;i=d[i].n){
			int y=d[i].b;
			if(y==fa||vis[i>>1]) continue;
			gt_rt(y,x,siz);sz[x]+=sz[y];
			int msz=max(siz-sz[y],sz[y]);
			if(msz<ssz) ssz=msz,rt=i;
		}
	}
	void dfs(int x,int fa,int s,int op){
		if(x<=n) val[x]=s+XS::dep[x],col[x]=op,q[++top]=x;
		for(int i=h[x];i;i=d[i].n){
			int y=d[i].b;
			if(y==fa||vis[i>>1]) continue;
			dfs(y,x,s+d[i].c,op);
		}
	}
	void solve(int x,int siz){
		ssz=1e9,gt_rt(x,0,siz);
		if(ssz==1e9) return ;
		int i=rt;top=0;vis[i>>1]=1;
		dfs(d[i].a,0,0,0),dfs(d[i].b,0,0,1);
		ans=max(ans,XS::work()+d[i].c);int sum=sz[d[i].b];
		solve(d[i].a,siz-sum),solve(d[i].b,sum);
	}
}
signed main(){
	scanf("%lld",&n);
	BFZ::init();
	XS::init();
	TREE::init();
	BFZ::solve(1,BFZ::tot);
	printf("%lld\n",ans); 
	return 0;
}

标签:lc,int,题解,tot,WC2018,dep,cun,id,通道
From: https://www.cnblogs.com/hubingshan/p/17929993.html

相关文章

  • ARC167D Good Permutation 题解
    ARC167D看到排列并且有\(i\getsa_i\),就可以直接建出图来,显然是若干个不相干的环。如果不求字典序最小,就可以直接不在同一个环中的\(i,j\)直接交换就可以了,因为它要求了最小化操作数。如果求字典序最小,直接从前往后扫一遍,可以用set维护不在这个环中且\(j>i\)的最小值,如果......
  • ARC105E Keep Graph Disconnected 题解
    ARC105E正向考虑是很难的,从结果入手,发现最后一定是分别包含\(1\),\(n\)的两个完全图。考虑表示出这两个人一共加了多少边:\(\frac{n(n-1)}{2}-m-x(n-x)\),\(x\)表示点\(1\)所在集合的大小。由于是判断先手还是后手必胜,所以只需看结果对\(2\)的余数,于是对\(n\)的奇偶进行......
  • P5513 [CEOI2013] Board 题解
    P5513容易发现,每次等价于对一个二进制数进行操作。但是这个二进制数长为\(n\),即需要高精。但是这样支持加一和减一是复杂度会退化为\(\mathcal{O}(n^2)\),有一个很正常的做法就压位,仿照bitset的做法进行操作,复杂度\(\mathcal{O}(\frac{n^2}{w})\)。这样已经可以通过了,但发......
  • 模拟赛简要题解
    11.16(C0389)100+10+50=160,rk3。本来BC都应该写出来的。A:dp或贪心都可以,贪心直接从下往上覆盖即可。B:注意:这里的\(\oplus\)指的是按位或。合法条件可以化简为:\(\oplus_{i=1}^{p}a_i=\oplus_{i=p+1}^{n}a_i\)。继续挖掘。看到位运算肯定想到拆位,考虑每一位第一次和......
  • 【数据结构】P4338 [ZJOI2018] 历史 题解
    P4338先考虑怎么安排崛起的先后顺序最优。但是发现好像没有一个很好的顺序去进行崛起,并且由于\(a_i\)的值域会很大,所以即使知道顺序应该也会难以进行维护。转换一下方向,正难则反。考虑每个点的贡献,但是颜色不同时只会算一次,所以要钦定是哪一个点造成的贡献。令当前考虑的点为......
  • CF1896D Ones and Twos 题解
    CF1896D如果只有单次询问其实可以双指针,但是这个难以进行拓展。考虑找点性质。发现\(a_i,v\in\{1,2\}\),从值域上下手。发现若存在和为\(S\)的方案,则一定有和为\(S-2\)的方案,因为可以直接\(-2\)或\(-1-1\)。然后就变为找最大的和为奇/偶数了,因为如果最大的都不行就肯定......
  • P9032 [COCI2022-2023#1] Neboderi 题解
    P9032考试题。发现\(g\)的值是若干个相同的段,且段数很少,因为每次取\(\gcd\)至少会将值域变为原来的一半。所以段数是\(\mathcal{O}(\logV)\)的。然后就可以从小到大枚举左端点,然后枚举\(g\)的值,找的是最远的满足\(\gcd(a_l,\dots,a_r)=g\)的\(r\),这里可以使用二分......
  • 做了网络隔离后,如何建立高效安全的数据安全交换通道?
    数据安全对企业生存发展有着举足轻重的影响,数据资产的外泄、破坏都会导致企业无可挽回的经济损失和核心竞争力缺失。数据流动才能让其释放价值,想要保护企业核心资产,就要实现数据安全交换。很多企业为了防止知识产权、商业机密数据泄露,通常会将自身网络进行安全性隔离。在内部实......
  • [CTSC2018]暴力写挂题解
    我们先将柿子变成\(\frac{1}{2}(dis_{x,y}+dep_{x}+dep_{y})-dep'_{lca'}\)考虑边分治,枚举断边,我们将一个点在第二棵树上的点权看成是\(v_x=d_x+dep_x\),答案就为\(v_x+v_y+dep'_{lca'}\)对于每次边分治将分治联通块内所有点在第二棵树上的建出虚树,同时将分治联通块以分治中心......
  • CF1887D Split 题解
    Problem-D-CodeforcesSplit-洛谷我现在水平好烂,再做下去自信心就全败没了先考虑\(Q=1\)怎么做?两种做法:暴力枚举分界点,左右判断暴力枚举\(\max\limits_{i=l}^{x}a_i\),找到最靠右边的分界点位置\(x\),判断是否\(\max\limits_{i=l}^{x}a_i<\min\limits......