题解 LGP8820【[CSP-S 2022] 数据传输】

posted on 2022-10-30 16:27:18 | under 题解 | source

problem

一棵 n 个点的树。如果两个点在树上的距离不超过 k,我们称这两个点可以互相传送信息。现在 q 次给定 s,t,选出 h={h1=s,h2,h3,,hc1,hc=t}c 个点,满足相邻两个点可以互相传送信息,请最小化 ihvi

前置知识

{max,+} 矩阵乘法 / 动态 DP。对于矩阵 a,b,c,这是我们所熟知的矩阵乘法 c=a×b

c=a×bci,j=kai,kbk,j.

现在我们定义矩阵 法:

c=abci,j=mink{ai,k+bk,j}.

为了更好地理解矩阵 法,您可以认为,ci,j 的值是 a 的第 i顺时针旋转 90 后与 b 的第 j 列依次相加取最小值的结果。如果某一个值是 代表强制不选,如果是 0 代表继承。

我们可以证明,矩阵 法没有交换律,但有结合律。

solution when k2

k=1 是【模板】链上求和。将 st 这条链拉出来,记链上第 i 个点的答案为 fi,我们不妨把 fi 写成矩阵 法之形式。

[fi1][vhi]=[fi].

k=2 时一定会在链上走,如果走下去了回不去,答案更劣。

[fi1,fi2][vhi,0vhi,]=[fi,fi1].


(走下去再返回(即红边)不如不走(即绿边))

solution when k=3

k=3 时最多向下走一步,不然就走不出去了。令 fi,0 表示在点 i 的最小代价,fi,1 表示在 i 的某一个儿子(可以算上父亲,这不重要)上的最小代价。

{bi=minj是i的儿子或父亲vj.fi,0=min{fi3,0,fi2,0,fi2,1,fi1,0,fi1,1}+vhi.fi,1=min{fi2,0,fi1,0,fi1,1}+bhi.

考虑优化:我们不太关心这些点在链上还是不在链上。令 fi,j 表示现在在离第 i 个点距离为 j 的点上。重定义 bi=minj是i的儿子vj

[fi1,0,fi1,1,fi1,2][vhi,0,vhi,bhi,0vhi,,]=[fi,0,fi,1,fi,2]

(这里转移不用关心走到 i1 的情况,已经在 fi1,0 那里算过了,不用管)

(这里 bi 仅限儿子,在 LCA 处要稍微动一下,加上父亲的贡献)

(为了不分讨,可以强行认为 f0=[,,0],尽管它并没有实际意义)

于是你会了暴力。

solution 的优化

考虑把这玩意多组询问。

观察到每一个转移矩阵只和 hi,vhi,bhi 有关,而后两个可以预处理,那么转移矩阵可以预处理。

模仿倍增 LCA,我们也整一个 dwi,j,upi,j,分别表示以 i 为最后一个点,从上往下 / 从下往上数 2j 个父亲的矩阵 和。转移可以从 2j1 那里拼起来,O(nk3logn);询问可以把路径拆成 slca(s,t),lca(s,t),lca(s,t)t 三段,分别 O(k3logn) 的求出矩阵 和,求出答案即可。

总的复杂度为 O((n+q)k3logn)

code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef long long LL;
template<int N,int M,class T=LL> struct matrix{
	T a[N][M];
	matrix(T flag=1e18){memset(a,0x3f,sizeof a);for(int i=0;i<N&&i<M;i++) a[i][i]=flag;}
	T* operator[](int i){return a[i];}
};
template<int N,int M,int R,class T=LL> matrix<N,R,T> operator*(matrix<N,M,T> a,matrix<M,R,T> b){
	matrix<N,R,T> c=1e18;
	for(int i=0;i<N;i++){
		for(int j=0;j<M;j++){
			for(int k=0;k<R;k++){
				c[i][k]=min(c[i][k],a[i][j]+b[j][k]);
			}
		}
	}
	return c;
};
template<int N,int M,class T=int> struct graph{
	int head[N+10],nxt[M*2+10],cnt;
	struct edge{
		int u,v; T w;
		edge(int u=0,int v=0,T w=0):u(u),v(v),w(w){}
	} e[M*2+10];
	graph(){memset(head,cnt=0,sizeof head);}
	edge& operator[](int i){return e[i];}
	void add(int u,int v,T w=0){e[++cnt]=edge(u,v,w),nxt[cnt]=head[u],head[u]=cnt;}
	void link(int u,int v,T w=0){add(u,v,w),add(v,u,w);}
};
LL a[200010],b[200010];
int n,m,sshwy,fa[19][200010],dep[200010];
graph<200010,200010> g;
matrix<3,3> dw[19][200010],up[19][200010];
matrix<1,3> f0;
matrix<3,3> gettrans(int i,LL del=1e18){
	matrix<3,3> c=1e18; 
	switch(sshwy){
		case 1: c[0][0]=a[i]; break;
		case 2: c[0][1]=0,c[0][0]=c[1][0]=a[i]; break;
		case 3: c[0][0]=c[1][0]=c[2][0]=a[i],c[0][1]=c[1][2]=0,c[1][1]=min(b[i],del); break;
	}
	return c;
}
void dfs(int u,int f=0){
	dep[u]=dep[fa[0][u]=f]+1,b[u]=1e18;
	for(int i=g.head[u];i;i=g.nxt[i]){
		int v=g[i].v; if(v==f) continue;
		dfs(v,u),b[u]=min(b[u],a[v]);
	}
	dw[0][u]=up[0][u]=gettrans(u);
}
int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v); int d=dep[u]-dep[v];
	for(int j=18;j>=0;j--) if(d>>j&1) u=fa[j][u];
	if(u==v) return u;
	for(int j=18;j>=0;j--) if(fa[j][u]!=fa[j][v]) u=fa[j][u],v=fa[j][v];
	return fa[0][u];
}
matrix<3,3> query_dw(int u,int k){
	matrix<3,3> res=0;
	for(int j=18;j>=0;j--) if(k>>j&1) res=dw[j][u]*res,u=fa[j][u];
	return res;
}
matrix<3,3> query_up(int u,int k){
	matrix<3,3> res=0;
	for(int j=18;j>=0;j--) if(k>>j&1) res=res*up[j][u],u=fa[j][u];
	return res;
}
LL query(int u,int v){
	int k=lca(u,v);
	matrix<1,3> res=f0*query_up(u,dep[u]-dep[k])*gettrans(k,a[fa[0][k]])*query_dw(v,dep[v]-dep[k]) 
	return min({res[0][0]-a[v],res[0][1],res[0][2]})+a[v];
}
int main(){
	scanf("%d%d%d",&n,&m,&sshwy);
	f0[0][sshwy-1]=0,a[0]=1e18;
	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),g.link(u,v);
	dfs(1);
	for(int j=1;j<=18;j++){
		for(int i=1;i<=n;i++) fa[j][i]=fa[j-1][fa[j-1][i]];
		for(int i=1;i<=n;i++) dw[j][i]=dw[j-1][fa[j-1][i]]*dw[j-1][i];
		for(int i=1;i<=n;i++) up[j][i]=up[j-1][i]*up[j-1][fa[j-1][i]];
	}
	for(int u,v;m--;) scanf("%d%d",&u,&v),printf("%lld\n",query(u,v));
	return 0;
}

posted @   caijianhong  阅读(104)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示