P8820 [CSP-S 2022] 数据传输
tmuxvs5t
·
·
题解
读题
读完题之后 k=1 就会了。
可以说是白送的。
讨论 k=2,k=3 时的情况。
当 k=2 时:
假设结点 u 转移到不在询问链上的节点 k。
我们可以发现,绕两步还不如一步直接走来的快。
换句话说就是 u-k-v 不如 u-v
所以我们可以判定 k=2 时仍然可以在单个链上dp。
由于没有节点的值是负的,k=2 情况似乎也解决了,那就是在这条链上线性dp。
至此,恭喜你拿到了所有暴力分。
当 k=3 时:
发现此时貌似可以转移到链的外部。
具体来说是这样的:
红色的是naive的转移。
绿色是贪心的转移。
蓝色是另一种转移。
可以发现,使用蓝色策略似乎可以绕过一些点,使得答案更优。
所以考虑设计一个dp来计算这件事。
设计 k=3 时的 dp
容易发现,跳出链的充要条件是和链的距离不超过1。
所以我们需要预处理出节点 u 周边权值最小的节点 w_u 。
一遍dfs即可。
接下来考虑设 dp_{u,k} 表示考虑到了节点 u ,目前距离节点 u 距离为 k 的dp最小值。
考虑 k=0,1,2 三种情况。
如上图,当 k=0 时,当前节点的值可以从前面的值(距离不超过3)里转移。
具体来说就是:
dp_{i,0}=\min\{dp_{i-1,0},dp_{i-1,1},dp_{i-1,2}\}+a_i
如上图,当 k=1 时,当前节点的值可以从前面的值里转移。
但是。。这次要分为两种情况。
当转移到左边黑方框时:
容易发现,此时 dp_{u,1}=dp_{u-1,0} 。
当转移到上面黑方框时:
注意我们在开篇谈到的问题。。
蓝色是另一种转移。
可以发现,使用蓝色策略似乎可以绕过一些点,使得答案更优。
蓝色策略是什么策略?
是跨度为3的转移。
所以我们断定,此时 dp_u=dp_{u-1,1}+w_u 。
综合一下,得到此状态最终解 dp_u=\min\{dp_{u-1,0},dp_{u-1,1}+w_u\} 。
不会放过任何一个使萌新懵圈的点。
为什么可以直接用 w_u ?
我们在开篇定义的是 w_u 表示为 节点 u 周边权值最小的节点 w_u 。
而现在我们期望的是除了 u-1,u+1 以外最小的节点。
为什么这个地方可以 ignore 过去?
由于我们的dp认为 w_u 就是 除了 u-1,u+1 以外最小的节点,
所以我们考虑将 u-1,u+1 分别赋予这个节点。
我们考虑让 w_u=u-1 ,可以发现,u上方的值只能是 f_k+f_{u-1} ,而u左边的值可以是 f_k+f_{u-1} 。在同一起跑线的(深度相同)情况下,左侧的转移严格不劣。
也就是说上方的 w_u 转移完全不会对答案产生任何影响。ignore掉。
再次考虑让 w_u=u+1 , 比起上次,除了 w_u 转移的深度更靠后的以外,基本没什么区别。
综上,我们可以严格证明出使用 w_u 代替 “节点 u 周边权值最小的节点 w_u”的完全合理的。

如上图,当 $k=2$ 时,当前节点的值可以从前面的值里转移。
直接得到 $dp_{u,2}=dp_{u-1,1}$ 。
然后就结束了。
至此,该题的思维难点全部结束。
----------------------------------------
接下来就是~~无脑~~的优化&套路环节了。
我们考虑用矩阵来表示每整个转移环节。
定义广义矩阵乘法 $C_{i,j}=\min\{a_{i,k}+b_{k,j}\}$ ,满足结合律。
设开始时的向量为
$$
\left[a_1,0,0\right]
$$
转移矩阵就是
$$
\left[
\begin{array}{l}
a_i& 0& \infty \\
a_i& w_i& \infty \\
a_i& \infty& \infty \\
\end{array}
\right]
$$
倍增维护从上到下、从下到上的矩阵乘积,扩展进倍增LCA即可。
(剩下的两种情况也可以用矩阵转移,比较简单,可以自己推一下)
代码:
```cpp
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define per(a,b,c) for(int a=b;a>=c;a--)
#define pb push_back
#define int long long
int n,Q,k;
int va[N];
vector<int> v[N];
struct mat{
int rc[3][3];
mat operator*(mat b){
mat nw;
rep(i,0,2){
rep(j,0,2){
int res=rc[i][1]+b.rc[1][j];
rep(k,0,2){
res=min(res,rc[i][k]+b.rc[k][j]);
}
nw[i][j]=res;
}
}
return nw;
}
int* operator[](int k){return rc[k];}
};
mat Ii;
const int maxlog=18;
int vis[N],fa[N][19],dep[N],w[N];
void dfs(int u,int dp){
dep[u]=dp;
vis[u]=1;
for(auto i:v[u]){
if(!vis[i]){fa[i][0]=u;w[i]=min(w[i],va[u]);w[u]=min(w[u],va[i]);dfs(i,dp+1);}
}
}
mat mem(){
mat ir;
rep(i,0,2){
rep(j,0,2){
ir[i][j]=1e18;
}
}return ir;
}
mat up[N][19],down[N][19];
void init(int x){
up[x][0]=mem();
if(k==1){up[x][0][0][0]=va[x];}
if(k==2){up[x][0][0][0]=up[x][0][1][0]=va[x];
up[x][0][0][1]=0;}
if(k==3){up[x][0][0][0]=up[x][0][1][0]=up[x][0][2][0]=va[x];
up[x][0][0][1]=up[x][0][1][2]=0;
up[x][0][1][1]=w[x];}
//
down[x][0]=mem();
if(k==1){down[x][0][0][0]=va[x];}
if(k==2){down[x][0][0][0]=down[x][0][1][0]=va[x];
down[x][0][0][1]=0;}
if(k==3){down[x][0][0][0]=down[x][0][1][0]=down[x][0][2][0]=va[x];
down[x][0][0][1]=down[x][0][1][2]=0;
down[x][0][1][1]=w[x];}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
per(i,maxlog,0)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
per(i,maxlog,0){
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
}return fa[x][0];
}
mat jmp(int x,int y,int mode){
mat res=Ii;
if(dep[x]<=dep[y])return res;
for(int i=maxlog;i>=0;i--){
if(dep[fa[x][i]]>=dep[y]){
res=mode?(down[x][i]*res):(res*up[x][i]);x=fa[x][i];
}
}return res;
}
mat query(int s,int t){
if(s==t||fa[s][0]==t||fa[t][0]==s)return Ii;
auto lc=lca(s,t);mat mid=((lc^s)&&(lc^t))?up[lc][0]:Ii;
return jmp(fa[s][0],lc,0)*mid*jmp(fa[t][0],lc,1);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
Ii=mem();Ii[0][0]=Ii[1][1]=Ii[2][2]=0;
cin>>n>>Q>>k;
rep(i,1,n){
cin>>va[i];
w[i]=1e18;
}
rep(i,1,n-1){
int x,y;cin>>x>>y;
v[x].pb(y);v[y].pb(x);
}
dfs(1,1);
rep(k,1,maxlog){
rep(i,1,n) fa[i][k]=fa[fa[i][k-1]][k-1];
}
rep(i,1,n)init(i);
rep(k,1,maxlog){
rep(i,1,n){
up[i][k]=up[i][k-1]*up[fa[i][k-1]][k-1];
down[i][k]=down[fa[i][k-1]][k-1]*down[i][k-1];
}
}
while(Q--){
int u,v;cin>>u>>v;
mat beg=mem();beg[0][0]=va[u];
beg=beg*query(u,v)*up[v][0];
cout<<beg[0][0]<<endl;
}
}
```