P8820 [CSP-S 2022] 数据传输

· · 题解

读题

读完题之后 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”的完全合理的。

![](https://cdn.luogu.com.cn/upload/image_hosting/44b7djr4.png) 如上图,当 $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; } } ```