动态DP随想

前言

我是被逼的
不打还真不会打
模版题

干啥的

树上最大独立集?
动态改点权?
NOIP2018D2T3?

玩法

以模版题为例
先写出 d p dp dp方程,常用的 f i , 0 / 1 f_{i,0/1} fi,0/1表示当前点选了/没选的最大方案
转移有
f i , 0 = ∑ m a x ( f s o n , 0 , f s o n , 1 ) f_{i,0}=\sum max(f_{son,0},f_{son,1}) fi,0=max(fson,0,fson,1)
f i , 1 = c a l i + ∑ f s o n , 0 f_{i,1}=cal_i+\sum f_{son,0} fi,1=cali+fson,0

l d p i , 0 / 1 ldp_{i,0/1} ldpi,0/1表示仅考虑轻子树,自己选/=不选的最大方案
先改变矩阵的定义,原本的乘变成加,求和变成取max
按照倍增floyd的方式,可证这个也是满足结合律的

常规套路写成矩阵
[ l d p f a , 0 l d p f a , 0 l d p f a , 1 − I N F ] ∗ [ f i , 0 f i , 1 ] = [ f f a , 0 f f a , 1 ] \left[ \begin{matrix}ldp_{fa,0}&ldp_{fa,0}\\ldp_{fa,1}&-INF \end{matrix} \right] *\left[ \begin{matrix} f_{i,0}\\f_{i,1}\end{matrix}\right ]= \left[ \begin{matrix}f_{fa,0}\\f_{fa,1}\end{matrix}\right] [ldpfa,0ldpfa,1ldpfa,0INF][fi,0fi,1]=[ffa,0ffa,1]
注意这是一条重链上的转移,在重链上越靠上的点的矩阵乘在最前面
常规做法就用树剖优化可以做到 8 n l o g 2 n 8nlog^2n 8nlog2n

全局平衡二叉树

据说这玩意在07年的论文就有了?
类似一个静态的LCT
先说建法

先把树常规剖分一下,然后对重链建一个非完美的BST
对于每个点,我们设他的权为 ∑ s i z [ l i g h t s o n ] \sum{siz[lightson]} siz[lightson],即为轻儿子的子树大小
对于每条重链,我们找到其带权重心

for(int x=l;;x=preson[x])
{
	sum+=lsiz[x];
	if(x==r)break;
}
for(int x=l,num=lsiz[x];;x=preson[x],num+=lsiz[x])
{
	if(num*2>=sum){u=x;break;}
	if(x==r)break;
}

是一个类似这样的过程
然后递归两边继续建树
然后每个点有至多两个实儿子,就是他两边的中心

按照LCT的玩法,还有轻边这种
轻边就是原树上的轻儿子连向父亲的边…这个记录一下就好了
虽然对于一条重链可能会建成一个非常左倾/右倾的二叉树
不慌,分析一下复杂度

当我们跳轻边的时候,子树大小至少增长一倍
当我们跳BST上的边的时候,轻子树大小同样至少增加一倍
那么感性认知一下,这棵树的树高大概是log级别的…

然后我们就可以愉快的开展非法操作了

对于每个点,记录两个矩阵
m 1 m1 m1表示他的子树的矩阵乘积
m 2 m2 m2表示他的轻子树与他自己构成的 l d p i , 0 / 1 ldp_{i,0/1} ldpi,0/1的权值
更新的话有一个小小的骚操作可以减点常数qwq…
一定要非常注意矩阵乘的顺序,左儿子先乘自己再乘右儿子
更新就一直跳上去更新就好了
注意跳BST上的边的时候仅需要更新自己子树的 m 1 m1 m1,否则要先把自己给父亲的贡献去掉,再更新子树 m 1 m1 m1,再把贡献打回到父亲的 m 2 m2 m2

这个玩意还能询问子树 d p dp dp值,询问这个的话你就要找到这个点然后去他BST上的右孩子找到他的 m 1 m1 m1再乘上我的 m 2 m2 m2,注意不能直接用自己的 m 1 m1 m1来算答案
当然一般都是询问 r o o t root root的答案,此时直接回答就好

代码

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
    int f=1,x=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int stack[20];
inline void write(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=100005;
struct egde{int x,y,next;}a[2*MAXN];int len,last[MAXN];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
struct matrix
{
    int m[2][2];
    matrix(){m[0][0]=m[1][1]=0;m[0][1]=m[1][0]=-(1<<30);}
    friend matrix operator *(matrix u,matrix v)
    {
        matrix ret;
        for(int i=0;i<=1;i++)for(int j=0;j<=1;j++)for(int k=0;k<=1;k++)
            ret.m[i][k]=max(ret.m[i][k],u.m[i][j]+v.m[j][k]);
        return ret;
    }
    int getmx(){return max(max(m[0][0],m[0][1]),max(m[1][0],m[1][1]));}
};
int preson[MAXN],up[MAXN],siz[MAXN],lsiz[MAXN];
void pre_tree_node(int x)
{
    siz[x]=1;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y!=up[x])
        {
            up[y]=x;
            pre_tree_node(y);
            if(siz[y]>siz[preson[x]])preson[x]=y;
            siz[x]+=siz[y];
        }
    }
	lsiz[x]=siz[x]-siz[preson[x]];
}
int cal[MAXN],si[MAXN],root;
int n,m;
struct BST
{
    int son[MAXN][2],fa[MAXN];
    matrix m1[MAXN],m2[MAXN];//记录子树乘积  记录自己信息(轻子树) 
    void updata(int x)
    {
        m1[x]=m1[son[x][0]]*m2[x];
        m1[x]=m1[x]*m1[son[x][1]];
    }//注意矩阵乘的方法 
    bool isroot(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
    int buildtree(int l,int r)//父亲->儿子 
    {
        if(l>r||!l)return 0;
        int u=0,sum=0;
        for(int x=l;;x=preson[x])
        {
        	sum+=lsiz[x];
        	if(x==r)break;
        }
        for(int x=l,num=lsiz[x];;x=preson[x],num+=lsiz[x])
        {
            if(num*2>=sum){u=x;break;}
			if(x==r)break;
        }
        son[u][0]=buildtree(l,up[u]);son[u][1]=buildtree(preson[u],r);
        if(son[u][0])fa[son[u][0]]=u;
        if(son[u][1])fa[son[u][1]]=u;
        return u;
    }
    int init()
    {
        int ret;
        for(int i=1;i<=n;i++)m1[i].m[0][0]=m1[i].m[0][1]=m1[i].m[1][0]=0,m1[i].m[1][1]=-(1<<30);
        for(int i=1;i<=n;i++)fa[i]=up[i],m2[i].m[0][0]=m2[i].m[1][0]=m2[i].m[0][1]=0,m2[i].m[1][1]=-(1<<30);
        for(int i=1;i<=n;i++)if(preson[up[i]]!=i)
        {
            int L=i,R;
            for(R=i;preson[R];R=preson[R]);
            int u=buildtree(L,R);
            fa[u]=up[i];
            if(!up[i])ret=u;
        }
        return ret;
    }
    void modify(int x,int W)
    {
        m2[x].m[1][0]+=W-cal[x];cal[x]=W;
        for(;x!=0;x=fa[x])
        {
            if(isroot(x)&&fa[x])
            {
                m2[fa[x]].m[0][0]-=max(m1[x].m[0][0],m1[x].m[1][0]);m2[fa[x]].m[0][1]=m2[fa[x]].m[0][0];
                m2[fa[x]].m[1][0]-=m1[x].m[0][0];
                updata(x);
                m2[fa[x]].m[0][0]+=max(m1[x].m[0][0],m1[x].m[1][0]);m2[fa[x]].m[0][1]=m2[fa[x]].m[0][0];
                m2[fa[x]].m[1][0]+=m1[x].m[0][0];
            }
            else updata(x);
        }
    }
}bst;
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;i++)si[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        ins(x,y);ins(y,x);
    }
    pre_tree_node(1);
    root=bst.init();
    for(int i=1;i<=n;i++)
        bst.modify(i,si[i]);
//    int lastans=0;
    while(m--)
    {
        int x=read(),y=read();
        bst.modify(x,y);
        pr2(bst.m1[root].getmx());
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值