题目链接
题目解法
首先这道题带修改的版本是 LuoguP4719 【模板】“动态 DP”&动态树分治
这一题不带修改,也可以用 P4719题解 中的方法解决,但可以考虑更多的做法
朴素的 d p dp dp 很简单,略过
考虑倍增
每次修改只会修改两个点
u
,
v
u,v
u,v 的祖先的
d
p
dp
dp 值
考虑维护转移矩阵,
M
a
t
u
Mat_u
Matu 表示
f
a
u
fa_u
fau 从不包括
u
u
u 的其他儿子转移而来的转移矩阵
这样定义有 2 个好处
- 可以更方便修改,只需要修改 2 条链上的 d p dp dp 值即可
- 可以进行倍增,若 u u u 是 v v v 的祖先,那么 d p dp dp 矩阵 f u = M a t v − > u ∗ f v f_u=Mat_{v->u}*f_v fu=Matv−>u∗fv,其中 M a t v − > u Mat_{v->u} Matv−>u 表示转移矩阵的乘积(另外一提, m i n , + min,+ min,+ 矩阵满足结合律,但不满足交换律,所以写倍增时一定要当心矩乘顺序)
之后简单了,维护
M
a
t
u
,
i
Mat_{u,i}
Matu,i 表示从
u
u
u 往上走
2
i
2^i
2i 步的转移矩阵的乘积
然后对于
a
,
b
a,b
a,b 分类讨论,以下假设
d
e
p
t
h
u
≤
d
e
p
t
h
v
depth_u\le depth_v
depthu≤depthv
- u u u 是 v v v 的祖先,具体过程不细说,直接上代码:
Matrix cur=f[b];cur.a[y^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[b][i]]>=depth[lca]) cur=trans[b][i]*cur,b=up[b][i];
cur.a[x^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[b][i]]) cur=trans[b][i]*cur,b=up[b][i];
- 反之,对于 u , v u,v u,v 分别维护到 l c a lca lca 的 d p dp dp 值, l c a lca lca 出手动转一下,上面直接倍增,代码:
Matrix cur1;cur1=f[a],cur1.a[x^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[a][i]]>depth[lca]) cur1=trans[a][i]*cur1,a=up[a][i];
cur1=trans[a][0]*cur1;
Matrix cur2;cur2=f[b],cur2.a[y^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[b][i]]>depth[lca]) cur2=trans[b][i]*cur2,b=up[b][i];
Matrix cur;cur.n=2,cur.m=1,cur.a[0][0]=cur1.a[0][0]-f[b].a[1][0]+cur2.a[1][0];
cur.a[1][0]=cur1.a[1][0]-min(f[b].a[0][0],f[b].a[1][0])+min(cur2.a[0][0],cur2.a[1][0]);
for(int i=18;i>=0;i--) if(depth[up[lca][i]]) cur=trans[lca][i]*cur,lca=up[lca][i];
时间复杂度 O ( 2 3 n l o g n ) O(2^3nlogn) O(23nlogn)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N(100100),inf(1e15);
struct Matrix{ int n,m,a[2][2];};
Matrix operator *(const Matrix &A,const Matrix &B){
Matrix C;C.n=A.n,C.m=B.m;C.a[0][0]=C.a[0][1]=C.a[1][0]=C.a[1][1]=inf;
for(int i=0;i<C.n;i++) for(int j=0;j<C.m;j++)
for(int k=0;k<A.m;k++) C.a[i][j]=min(C.a[i][j],A.a[i][k]+B.a[k][j]);
return C;
}
int n,m,p[N],depth[N],up[N][20];
Matrix trans[N][20],f[N];
char type[2];
int e[N<<1],ne[N<<1],h[N],idx;
inline int read(){
int FF=0,RR=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
return FF*RR;
}
void add(int x,int y){ e[idx]=y,ne[idx]=h[x],h[x]=idx++;}
void dfs(int u,int fa){
auto &t=f[u];t.n=2,t.m=1;
t.a[0][0]=0,t.a[1][0]=p[u],depth[u]=depth[fa]+1;
for(int i=h[u];~i;i=ne[i]){
int v=e[i];
if(v!=fa){
up[v][0]=u,dfs(v,u);
t.a[0][0]+=f[v].a[1][0],t.a[1][0]+=min(f[v].a[0][0],f[v].a[1][0]);
}
}
}
int get_lca(int x,int y){
for(int i=18;i>=0;i--) if(depth[up[y][i]]>=depth[x]) y=up[y][i];
if(x==y) return x;
for(int i=18;i>=0;i--) if(up[x][i]!=up[y][i]) x=up[x][i],y=up[y][i];
return up[x][0];
}
signed main(){
n=read(),m=read(),scanf("%s",type);
for(int i=1;i<=n;i++) p[i]=read();
memset(h,-1,sizeof(h));
for(int i=1;i<n;i++){
int u=read(),v=read();
add(u,v),add(v,u);
}
dfs(1,0);
for(int i=1;i<=n;i++){
auto &t=trans[i][0];t.n=t.m=2;
t.a[0][0]=inf,t.a[0][1]=f[up[i][0]].a[0][0]-f[i].a[1][0],t.a[1][0]=t.a[1][1]=f[up[i][0]].a[1][0]-min(f[i].a[0][0],f[i].a[1][0]);
for(int j=1;j<=18;j++) for(int i=1;i<=n;i++){
up[i][j]=up[up[i][j-1]][j-1];
trans[i][j]=trans[up[i][j-1]][j-1]*trans[i][j-1];
}
while(m--){
int a=read(),x=read(),b=read(),y=read();
if(depth[a]>depth[b]) swap(a,b),swap(x,y);
int lca=get_lca(a,b);
if(lca==a){
Matrix cur=f[b];cur.a[y^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[b][i]]>=depth[lca]) cur=trans[b][i]*cur,b=up[b][i];
cur.a[x^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[b][i]]) cur=trans[b][i]*cur,b=up[b][i];
if(min(cur.a[0][0],cur.a[1][0])>=inf) puts("-1");
else printf("%lld\n",min(cur.a[0][0],cur.a[1][0]));
}
else{
Matrix cur1;cur1=f[a],cur1.a[x^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[a][i]]>depth[lca]) cur1=trans[a][i]*cur1,a=up[a][i];
cur1=trans[a][0]*cur1;
Matrix cur2;cur2=f[b],cur2.a[y^1][0]=inf;
for(int i=18;i>=0;i--) if(depth[up[b][i]]>depth[lca]) cur2=trans[b][i]*cur2,b=up[b][i];
Matrix cur;cur.n=2,cur.m=1,cur.a[0][0]=cur1.a[0][0]-f[b].a[1][0]+cur2.a[1][0];
cur.a[1][0]=cur1.a[1][0]-min(f[b].a[0][0],f[b].a[1][0])+min(cur2.a[0][0],cur2.a[1][0]);
for(int i=18;i>=0;i--) if(depth[up[lca][i]]) cur=trans[lca][i]*cur,lca=up[lca][i];
if(min(cur.a[0][0],cur.a[1][0])>=inf) puts("-1");
else printf("%lld\n",min(cur.a[0][0],cur.a[1][0]));
}
}
return 0;
}