文章目录
- 题目
- 思路
- 代码
题目
Luogu-传送门
题目大意:给你一棵树进行染色,iii 号节点染色费用为 pip_ipi?,要求相邻两个节点必须有一个要染色,现在给出 mmm 个询问,分别要求两个节点必须(染|不染)色,对于每个询问求出最小染色代价
数据范围:
1≤n,m≤100000,1≤pi≤1000001\le n,m\le100000 ,1≤p_i≤1000001≤n,m≤100000,1≤pi?≤100000
思路
首先我在考场上想出了 dpdpdp 44分的做法,对于每次询问单独重新计算:
定义f[u][0∣1]:以u为根子树且u节点(染∣不染)时该子树最小代价定义\quad f[u][0|1]:以u为根子树且u节点(染|不染)时该子树最小代价定义f[u][0∣1]:以u为根子树且u节点(染∣不染)时该子树最小代价
转移也很简单:
①当 uuu 为叶节点时:
f[u][0]={0(u点无要求∣要求不选)INF(u点要求选)f[u][1]={pu(u点无要求∣要求选)INF(u点要求不选)f[u][0]= \begin{cases} 0\quad (u点无要求|要求不选)\\ INF\quad (u点要求选) \end{cases}\\ f[u][1]= \begin{cases} p_u\quad (u点无要求|要求选)\\ INF\quad (u点要求不选) \end{cases} f[u][0]={
0(u点无要求∣要求不选)INF(u点要求选)?f[u][1]={
pu?(u点无要求∣要求选)INF(u点要求不选)?
②当 uuu 为非叶节点时:
f[u][0]=∑v∈sonuf[v][1]f[u][1]=∑v∈sonumin(f[v][0],f[v][1])f[u][0]=\sum_{v∈son_u}f[v][1]\\f[u][1]=\sum_{v∈son_u}min(f[v][0],f[v][1])f[u][0]=v∈sonu?∑?f[v][1]f[u][1]=v∈sonu?∑?min(f[v][0],f[v][1])
最终答案为: min(f[1][0],f[1][1])min(f[1][0],f[1][1])min(f[1][0],f[1][1])(从1开始DFS)
然后我们发现其实每次我们多做了许多次dp,真正每次更新的 dpdpdp 值只有这些:
如果你一开始就Dp,然后动态更新好像部分分会多点,但仍然是 O(n2)O(n^2)O(n2)的复杂度,所以我们考虑加快Dp过程,而像这种有LcaLcaLca出现又没有更新树的一般考虑用倍增
怎么考虑呢?我们想啊,考虑一下这种定义:
f[i][j][0∣1][0∣1]:i节点(染∣不染),i的2j祖先节点(染∣不染)时i的2j祖先的最优Dp值f[i][j][0|1][0|1]:i节点(染|不染),i的2^j祖先节点(染|不染)时i的2^j祖先的最优Dp值f[i][j][0∣1][0∣1]:i节点(染∣不染),i的2j祖先节点(染∣不染)时i的2j祖先的最优Dp值
但是我们发现似乎无法进行合并,因为当由 2(j?1)2^(j-1)2(j?1) 转移 2j2^j2j 有包含冲突
于是我们考虑去除 u节点所在子树u节点所在子树u节点所在子树 的影响:
f[i][j][0∣1][0∣1]:if[i][j][0|1][0|1]:if[i][j][0∣1][0∣1]:i节点(染|不染),i的2j2^j2j祖先节点(染|不染)时i的2j2^j2j祖先的最优Dp值,即在整个祖先的子树中减去u的子树的影响.
那么这个信息就可以进行合并了,因为转移时正好接上
然后我们考虑爬树时怎么爬,这一点我想了很久…
因为可能加上这棵子树时会改变f数组的决策
那么我们爬树时用两种状态一起爬就是了(该节点染|不染)
注意:
1.f数组的初始化不要在DFS中弄,因为此时g还没更新完
2.倍增爬树时j不要枚举反了
3.LL注意一下
4,v是Lca时注意一下
发现用动态 DpDpDp 理解更好,因为发现 fff 的定义不太好理解:
显然有:
[fv,0,fv,1]?[wu,0,wu,1∞,wu,1]=[fu,0,fu,1]\begin{bmatrix} f_{v,0}, f_{v,1} \end{bmatrix} * \begin{bmatrix} w_{u,0} ,w_{u,1}\\ \infty,w_{u,1} \end{bmatrix}= \begin{bmatrix} f_{u,0}, f_{u,1} \end{bmatrix} [fv,0?,fv,1??]?[wu,0?,wu,1?∞,wu,1??]=[fu,0?,fu,1??]
记一开始矩阵为 PPP,那么 dpdpdp 本质就是:
P?Mfau?Mfafau?...P*M_{fa_u}*M_{fa_{fa_u}}*... P?Mfau???Mfafau????...
由于加法对 minminmin 有分配律,所以这个矩阵乘法有结合律,我们可以利用维护后面的矩阵合并之后的模样
然后 fff 本质是合并矩阵进行倍增转移
代码
#include<set>
#include<map>
#include<stack>
#include<ctime>
#include<cstdio>
#include<queue>
#include<cmath>
#include<vector>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
int f=1,x=0;char c=getchar(); while(c<'0'||'9'<c){
if(c=='-')f=-1;c=getchar();} while('0'<=c&&c<='9'){
x=x*10+c-'0';c=getchar();} return f*x;
}
#define MAXN 100000
#define INF 214748364700ll
int n,m,p[MAXN+5];
vector<int> G[MAXN+5];
inline void Addedge(int u,int v){
G[u].push_back(v); G[v].push_back(u); return ;
}
int dep[MAXN+5],fa[MAXN+5][20];
LL f[MAXN+5][20][2][2],g[MAXN+5][2];
void DFS(int u){
//f[i][j][0|1][0|1] int siz=G[u].size(); g[u][1]=p[u]; dep[u]=dep[fa[u][0]]+1; for(int i=0;i<siz;i++){
int v=G[u][i]; if(v==fa[u][0]) continue; fa[v][0]=u; DFS(v); g[u][0]+=g[v][1]; g[u][1]+=min(g[v][0],g[v][1]); }//!!return ;
}
void Prepare(){
/* puts(""); for(int i=1;i<=n;i++) printf("%lld %lld\n",g[i][0],g[i][1]); */for(int i=1;i<=n;i++){
f[i][0][0][0]=INF,f[i][0][1][0]=g[fa[i][0]][0]-g[i][1]; f[i][0][0][1]=f[i][0][1][1]=g[fa[i][0]][1]-min(g[i][0],g[i][1]); //printf("%lld %lld %lld %lld\n",f[i][j][0][0],f[i][j][0][1],f[i][j][1][0],f[i][j][1][1]); } for(int j=1;j<=int(log2(n));j++) for(int i=1;i<=n;i++){
int k=fa[i][j-1]; fa[i][j]=fa[fa[i][j-1]][j-1]; f[i][j][0][0]=min(f[i][j-1][0][0]+f[k][j-1][0][0],f[i][j-1][0][1]+f[k][j-1][1][0]); f[i][j][0][1]=min(f[i][j-1][0][0]+f[k][j-1][0][1],f[i][j-1][0][1]+f[k][j-1][1][1]); f[i][j][1][0]=min(f[i][j-1][1][0]+f[k][j-1][0][0],f[i][j-1][1][1]+f[k][j-1][1][0]); f[i][j][1][1]=min(f[i][j-1][1][0]+f[k][j-1][0][1],f[i][j-1][1][1]+f[k][j-1][1][1]); // printf("%lld %lld %lld %lld\n",f[i][j][0][0],f[i][j][0][1],f[i][j][1][0],f[i][j][1][1]); } return ;
}
LL LCA(int u,int x,int v,int y){
if(dep[u]<dep[v]) swap(u,v),swap(x,y); int d=dep[u]-dep[v]; LL Lca,u0=INF,u1=INF,v0=INF,v1=INF,l0=INF,l1=INF; !x?u0=g[u][0]:u1=g[u][1]; !y?v0=g[v][0]:v1=g[v][1]; //puts(""); for(int j=0;j<=int(log2(d));j++) if(d&(1<<j)){
LL t0=u0,t1=u1; u0=min(t0+f[u][j][0][0],t1+f[u][j][1][0]); u1=min(t0+f[u][j][0][1],t1+f[u][j][1][1]); u=fa[u][j]; //printf("%lld %lld\n",u0,u1); } //printf("%d %d*\n",u,v); if(u==v) Lca=u,!y?l0=u0:l1=u1; else{
for(int j=int(log2(n));j>=0;j--)//!!if(fa[u][j]!=fa[v][j]){
LL t0=u0,t1=u1; u0=min(t0+f[u][j][0][0],t1+f[u][j][1][0]); u1=min(t0+f[u][j][0][1],t1+f[u][j][1][1]); t0=v0,t1=v1; v0=min(t0+f[v][j][0][0],t1+f[v][j][1][0]); v1=min(t0+f[v][j][0][1],t1+f[v][j][1][1]); u=fa[u][j],v=fa[v][j]; } Lca=fa[u][0]; l0=g[Lca][0]-g[u][1]-g[v][1]+u1+v1; l1=g[Lca][1]-min(g[u][0],g[u][1])-min(g[v][0],g[v][1])+min(u0,u1)+min(v0,v1); } //printf("***%lld %lld %lld\n",Lca,l0,l1); d=dep[Lca]-dep[1];for(int j=0;j<=int(log2(d));j++)if(d&(1<<j)){
LL t0=l0,t1=l1;l0=min(t0+f[Lca][j][0][0],t1+f[Lca][j][1][0]);l1=min(t0+f[Lca][j][0][1],t1+f[Lca][j][1][1]);Lca=fa[Lca][j];}LL ret=min(l0,l1);return ret>=INF?-1:ret;
}
int main(){
char Re[5];n=read(),m=read(),scanf("%s",Re);for(int i=1;i<=n;i++)p[i]=read();for(int i=1;i<n;i++){
int u=read(),v=read();Addedge(u,v);}DFS(1);Prepare();for(int i=1;i<=m;i++){
int a=read(),x=read(),b=read(),y=read();printf("%lld\n",LCA(a,x,b,y));}return 0;
}