动态dp

luogu4719是模板题了
看了RabitHu的题解

简言之

树链剖分
建一颗线段树维护该段[l,r]的部分答案([l,r]这段不一定联通)
部分答案:不包括重儿子的答案
这样就勉强维护了复杂度

矩阵乘法只是把转移方程写成矩阵的形式
无脑又好写
//Ctrl+V毫无压力

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
template <class T>inline void read(T &x){
    char c=nc();x=0;int f=1;
    for(;!isdigit(c);c=nc())if(c=='-')f=-1;
    for(;isdigit(c);c=nc())x=(x<<1)+(x<<3)+c-'0';
    x*=f;
}
template <class T>inline void write(T x){
    if(x<0)putchar('-'),x=-x;
    if(x>=10)write(x/10);
    putchar('0'+x%10);
}
const int N=100002;
int n,m,a[N],cnt,head[N],nex[N<<1],to[N<<1];
int fa[N],son[N],siz[N],top[N],idx[N],pos[N],tot,ed[N];
ll f[N][2];
struct matrix{
    ll g[2][2];
    matrix(){memset(g, 0, sizeof(g));}
    matrix operator*(const matrix &b)const{
        matrix c;
        for(int i=0;i<2;++i)
            for(int j=0;j<2;++j)
                for(int k=0;k<2;++k)
                    c.g[i][j]=max(c.g[i][j],g[i][k]+b.g[k][j]);
        return c;
    }
}val[N],data[N<<2];
inline void add(int u, int v){
    nex[++cnt]=head[u],head[u]=cnt,to[cnt]=v;
}
inline void init(){
    static int q[N];
    q[1]=1; 
    for(int ql=1,qr=1;ql<=qr;++ql)
        for(int u=q[ql],e=head[u],v;e;e=nex[e])
            if((v=to[e])!=fa[u])fa[v]=u,q[++qr]=v;
    for(int qr=n,u;qr;--qr){
        ++siz[u=q[qr]],siz[fa[u]]+=siz[u];
        if(siz[u]>siz[son[fa[u]]])son[fa[u]]=u;
    }
    for(int ql=1,u;ql<=n;++ql)
        if(!top[u=q[ql]]){
            for(int v=u;v;v=son[v])
                top[v]=u,idx[pos[v]=++tot]=v;
        ed[u]=tot;
    }
    for(int qr=n,u;qr;--qr){
        u=q[qr],f[u][1]=max(0,a[u]);
        for(int e=head[u],v;e;e=nex[e])
            if((v=to[e])!=fa[u])
                f[u][0]+=max(f[v][0],f[v][1]),f[u][1]+=f[v][0];
    }
}
inline void build(int k,int l,int r){
    if(l==r){
        ll g0=0,g1=a[idx[l]];
        for(int u=idx[l],e=head[u],v;e;e=nex[e])
            if((v=to[e])!=fa[u]&&v!=son[u])
                g0+=max(f[v][0],f[v][1]),g1+=f[v][0];
        data[k].g[0][0]=data[k].g[0][1]=g0;
        data[k].g[1][0]=g1,val[l]=data[k];
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid),build(k<<1|1,mid+1,r);
    data[k]=data[k<<1]*data[k<<1|1];
}
void change(int k,int l,int r,int p){
    if(l==r){data[k] = val[l];return;}
    int mid=(l+r)>>1;
    if(p<=mid)change(k<<1,l,mid,p);else change(k<<1|1,mid+1,r,p);
    data[k]=data[k<<1]*data[k<<1|1];
}
matrix query(int k,int l,int r,int ql,int qr){
    if(ql<=l&&qr>=r)return data[k];
    int mid=(l+r)>>1;
    if(qr<=mid)return query(k<<1,l,mid,ql,qr);
    if(ql>mid)return query(k<<1|1,mid+1,r,ql,qr);
    return query(k<<1,l,mid,ql,qr)*query(k<<1|1,mid+1,r,ql,qr);
}
matrix ask(int u){
    return query(1,1,n,pos[top[u]],ed[top[u]]);
}
inline void path_change(int u,int x){
    val[pos[u]].g[1][0]+=x-a[u],a[u]=x;
    matrix od,nw;
    while(u){
        od=ask(top[u]);
        change(1,1,n,pos[u]);
        nw=ask(top[u]);
        u=fa[top[u]];
        val[pos[u]].g[0][0]+=max(nw.g[0][0],nw.g[1][0])-max(od.g[0][0],od.g[1][0]);
        val[pos[u]].g[0][1]=val[pos[u]].g[0][0];
        val[pos[u]].g[1][0]+=nw.g[0][0]-od.g[0][0];
    }
}
int main(){
    read(n),read(m);
    for(int i=1;i<=n;++i)read(a[i]);
    for(int i=1,u,v;i<n;++i)read(u),read(v),add(u,v),add(v,u);
    init(),build(1,1,n);
    int u,x;matrix t;
    while(m--){
        read(u),read(x);
        path_change(u,x);
        t=ask(1);
        write(max(t.g[0][0], t.g[1][0])),puts("");
    }
    return 0;
}

转载于:https://www.cnblogs.com/MikuKnight/p/10046553.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值