Luogu P4719 动态dp

传送门

题解:
日常的写不动题,存板
假设这玩意不是动态的:
f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]为第i个点不选/选的答案。那么 f [ i ] [ 0 ] = ∑ max ⁡ ( f [ j ] [ 0 ] , f [ j ] [ 1 ] ) , f [ i ] [ 1 ] = ∑ f [ j ] [ 0 ] + v [ i ] . f[i][0]=\sum\max(f[j][0],f[j][1]),f[i][1]=\sum f[j][0]+v[i]. f[i][0]=max(f[j][0],f[j][1]),f[i][1]=f[j][0]+v[i].
然后修改一个点的权值最多对上面的值有影响。
然后再设 g [ i ] [ 0 / 1 ] g[i][0/1] g[i][0/1]为只考虑轻儿子的dp值。
于是 f [ i ] [ 0 ] = max ⁡ ( f [ s o n ] [ 0 ] , f [ s o n ] [ 1 ] ) + g [ i ] [ 0 ] , f [ i ] [ 1 ] = f [ s o n ] [ 0 ] + g [ i ] [ 1 ] . f[i][0]=\max(f[son][0],f[son][1])+g[i][0],f[i][1]=f[son][0]+g[i][1]. f[i][0]=max(f[son][0],f[son][1])+g[i][0],f[i][1]=f[son][0]+g[i][1].
然后利用人类智慧可以定义乘法为加法,定义加法为取max,搞出一个矩阵:
( f [ i ] [ 0 ] f [ i ] [ 1 ] ) = ( g [ i ] [ 0 ] g [ i ] [ 0 ] g [ i ] [ 1 ] − ∞ ) × ( f [ s o n ] [ 0 ] f [ s o n ] [ 1 ] ) \begin{pmatrix} f[i][0] \\ f[i][1] \end{pmatrix}=\begin{pmatrix} g[i][0] & g[i][0] \\ g[i][1] & -\infty \end{pmatrix}\times \begin{pmatrix} f[son][0] \\ f[son][1] \end{pmatrix} (f[i][0]f[i][1])=(g[i][0]g[i][1]g[i][0])×(f[son][0]f[son][1])
然后就可以树剖再上个线段树维护了。修改的时候改改重链f值改改链顶g值就行了。
不知道为毛这道题右下角取0都能过

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 100005
using namespace std;
int n,m,a[maxn],dep[maxn],fa[maxn],top[maxn],son[maxn],siz[maxn],tid[maxn],ed[maxn],rnk[maxn],ccnt,g[maxn][2];
struct node { int v; node *nxt; } edge[maxn*2],*head[maxn],*ncnt;
struct mat { int f[2][2]; } tree[maxn*4],val[maxn];
mat operator * (mat a,mat b)
{
    mat c;
    memset(c.f,0,sizeof(c.f));
    for(int k=0;k<2;k++)
        for(int i=0;i<2;i++)
            for(int j=0;j<2;j++) c.f[i][j]=max(c.f[i][j],a.f[i][k]+b.f[k][j]);
    return c;
}
void addedge(int u,int v)
{
    ncnt++;
    ncnt->v=v,ncnt->nxt=head[u];
    head[u]=ncnt;
}
void dfs1(int u,int f,int d)
{
    dep[u]=d,fa[u]=f,siz[u]=1,g[u][1]=a[u];
    for(node *p=head[u];p;p=p->nxt)
    {
        int v=p->v;
        if(v==f) continue;
        dfs1(v,u,d+1); siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) son[u]=v;
        g[u][0]+=max(g[v][0],g[v][1]),g[u][1]+=g[v][0];
    }
}
void dfs2(int u,int tp)
{
    tid[u]=++ccnt,rnk[ccnt]=u,top[u]=tp;
    if(!son[u]) { ed[u]=u; return; }
    dfs2(son[u],tp); ed[u]=ed[son[u]];
    for(node *p=head[u];p;p=p->nxt)
    {
        int v=p->v;
        if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
    }
}
void Build(int i,int l,int r)
{
    if(l==r)
    {
        int u=rnk[l],g0=0,g1=a[u];
        for(node *p=head[u];p;p=p->nxt)
        {
            int v=p->v;
            if(v!=fa[u]&&v!=son[u]) g0+=max(g[v][0],g[v][1]),g1+=g[v][0];
        }
        tree[i].f[0][0]=tree[i].f[0][1]=g0,tree[i].f[1][0]=g1;
        val[l]=tree[i];
        return;
    }
    int mid=(l+r)>>1;
    Build(i<<1,l,mid); Build(i<<1|1,mid+1,r);
    tree[i]=tree[i<<1]*tree[i<<1|1];
}
mat Query(int i,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr) return tree[i];
    int mid=(l+r)>>1;
    if(qr<=mid) return Query(i<<1,l,mid,ql,qr);
    else if(ql>mid) return Query(i<<1|1,mid+1,r,ql,qr);
    return Query(i<<1,l,mid,ql,qr)*Query(i<<1|1,mid+1,r,ql,qr);
}
void Modify(int i,int l,int r,int p)
{
    if(l==r) { tree[i]=val[l]; return; }
    int mid=(l+r)>>1;
    if(p<=mid) Modify(i<<1,l,mid,p);
    else Modify(i<<1|1,mid+1,r,p);
    tree[i]=tree[i<<1]*tree[i<<1|1];
}
void Modify(int u,int x)
{
    val[tid[u]].f[1][0]+=x-a[u]; a[u]=x;
    while(u)
    {
        mat la=Query(1,1,n,tid[top[u]],tid[ed[u]]);
        Modify(1,1,n,tid[u]);
        mat cur=Query(1,1,n,tid[top[u]],tid[ed[u]]);
        u=fa[top[u]];
        val[tid[u]].f[0][0]+=max(cur.f[0][0],cur.f[1][0])-max(la.f[0][0],la.f[1][0]);
        val[tid[u]].f[0][1]=val[tid[u]].f[0][0];
        val[tid[u]].f[1][0]+=cur.f[0][0]-la.f[0][0];
    }
}
int main()
{
    scanf("%d%d",&n,&m); ncnt=&edge[0];
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v); addedge(v,u);
    }
    dfs1(1,0,1); dfs2(1,1);
    Build(1,1,n);
    while(m--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Modify(x,y);
        mat t=Query(1,1,n,tid[1],tid[ed[1]]);
        printf("%d\n",max(t.f[0][0],t.f[1][0]));
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值