因为NOIP2018考了这一个东西,所以不得不学。
我们以这一题为例题来引入今天的学习:【模板】动态dp
我们显然可以用树形Dp去做,倒不如我们先把方程列出来。
这两条公式挺显然的吧。
假设我们现在无聊,往树链剖分的角度去考虑。
那么我们设一个数组g,表示的是从非重儿子转移来的状态,跟f数组的转移方程类似。
我们可以把上面两条公式改写成什么呢?
这两条也是挺显然的吧。
那么这个东西有什么用呢?
对于一条重链来说,g肯定是不会改变的,但是f是可能改变的。
我们试着去构造一个转移矩阵:
我们定义这个乘法的含义是,那么
然后化简出来出来就是上面那两条公式。
这个东西有什么用?
容易发现等于x所在重链底端到x的矩阵连乘积。这个矩阵指的是中间的哪一个矩阵,也就是对于x所在重链的底端到x的任一点p,。
这个也是挺显然的吧,因为每一个点存的都是非重儿子的答案,那么多个非重儿子的答案合并就变成了该子树的答案。
那么我们把一个点的权值变成了所在重链的乘积。(注意,上面的乘积和pi,都是新定义的*)
假设我们要改x点的权值,那么x所在的矩阵要修改吧,那么就变成了改点。
我们还可以求出不在这条重链上的第一个祖先,那么这个祖先的矩阵也要改,具体怎么改,可以通过原来x点所在重链的top的子树的答案的变化量来修改。
一直这样往上跳,一共会改log个节点,因为要访问区间连乘积,所以用线段树维护,时间复杂度就是:。
虽然树链剖分的常数不大,但是这个log平方的算法已经够卡常的了。
我还没有学会全局平衡二叉树。
#include
#include
#include
#include
#define lc (now<<1)
#define rc ((now<<1)|1)
using namespace std;
int n,m;
int w[100010];
struct edge{
int y,next;
}s[200010];
struct matrix{
int op[3][3];
matrix operator*(const matrix b)const{
matrix q;
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++){
q.op[i][j]=-1e9;
for(int k=1;k<=2;k++)
q.op[i][j]=max(q.op[i][j],op[i][k]+b.op[k][j]);
}
return q;
}
}tr[300010];
int first[100010],len=0;
int tot[100010],son[100010];
int image[100010],fact[100010],last[100010],fa[100010],top[100010];
int f[100010][2],g[100010][2];
void ins(int x,int y){
len++;
s[len]=(edge){y,first[x]};first[x]=len;
}
void dfs_1(int x){
tot[x]=1;
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(y==fa[x]) continue;
fa[y]=x;
dfs_1(y);
tot[x]+=tot[y];
if(tot[y]>tot[son[x]]) son[x]=y;
}
}
void dfs_2(int x,int tp){
image[x]=++len;fact[len]=x;top[x]=tp;
if(son[x]!=0) dfs_2(son[x],tp);
else {last[tp]=x;return ;}
for(int i=first[x];i!=0;i=s[i].next){
if(s[i].y==fa[x] || s[i].y==son[x]) continue;
dfs_2(s[i].y,s[i].y);
}
}
void tr_dp(int x){
f[x][1]=g[x][1]=w[x];
f[x][0]=g[x][0]=0;
for(int i=first[x];i!=0;i=s[i].next){
int y=s[i].y;
if(y==fa[x]) continue;
tr_dp(y);
f[x][0]+=max(f[y][0],f[y][1]);
f[x][1]+=f[y][0];
if(y==son[x]) continue;
g[x][0]+=max(f[y][0],f[y][1]);
g[x][1]+=f[y][0];
}
}
void update(int now,int l,int r,int x){
if(l==r){
tr[now].op[1][1]=tr[now].op[1][2]=g[fact[x]][0];
tr[now].op[2][1]=g[fact[x]][1];tr[now].op[2][2]=-1e9;
return ;
}
int mid=(l+r)/2;
if(x<=mid) update(lc,l,mid,x);
else update(rc,mid+1,r,x);
tr[now]=tr[lc]*tr[rc];
}
matrix query(int now,int x,int y,int l,int r){
if(x==l && y==r) return tr[now];
int mid=(l+r)/2;
if(y<=mid) return query(lc,x,y,l,mid);
else if(mid