Uoj 441 保卫王国

Uoj 441 保卫王国

  • 动态 \(dp\) .今天才来写这个题.
  • \(f[u][0/1]\) 表示子树 \(u\) 中不选/选 \(u\) 时的最小权值和,显然有:\(f[u][0]=\sum f[v][1] ,f[u][1]=w[u]+\sum \min(f[v][0],f[v][1])​\) .
  • 现在要资瓷修改 \(x\) 的点权 \(w[x]\) ,容易发现修改后只会影响 \(x\) 到根节点这一条链上的 \(f\) 值.若暴力更新这一条链,在树深度大时,时间复杂度仍是 \(O(nm)\) 的.考虑使用树剖来维护,尝试快速更新信息.
  • 由于树剖后,一个节点到根节点的路径上,轻边/重链都不会超过 \(logn\) 条,可以暴力修改轻儿子的贡献,用数据结构来维护重链.那么轻重儿子的信息需要分开存,用 \(g[u][0/1]\) 表示子树 \(u\) 除去重儿子的子树后, 不选/选 \(u\) 时的最小权值和.
  • 转移可以用下面这个转移矩阵表示,这里是 Min-plus matrix multiplication ,即将原来矩阵乘法的乘法换成加法,加法换成取 \(\min​\) .仍然满足结合律..(这东西还有其他用法,可以点进去看看)

1516579-20190303164911662-821234613.png

  • 用线段树维护区间矩阵乘积即可.

参考

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read()
{
    ll out=0,fh=1;
    char jp=getchar();
    while ((jp>'9'||jp<'0')&&jp!='-')
        jp=getchar();
    if (jp=='-')
        fh=-1,jp=getchar();
    while (jp>='0'&&jp<='9')
        out=out*10+jp-'0',jp=getchar();
    return out*fh;
}
const int MAXN=1e5+10;
int n,m;
int nx[MAXN<<1],to[MAXN<<1],head[MAXN],cnt=0;
inline void addedge(int u,int v)
{
    ++cnt;
    nx[cnt]=head[u];
    to[cnt]=v;
    head[u]=cnt;
    swap(u,v);
    ++cnt;
    nx[cnt]=head[u];
    to[cnt]=v;
    head[u]=cnt;
}
ll w[MAXN];
ll f[MAXN][2],g[MAXN][2];
int fa[MAXN],mxson[MAXN],siz[MAXN],dep[MAXN],dfn[MAXN],rnk[MAXN],top[MAXN],bot[MAXN],idx=0;
void DP(int u,int Fa)
{
    f[u][1]=w[u];
    for(int i=head[u];i;i=nx[i])
        {
            int v=to[i];
            if(v==Fa)
                continue;
            DP(v,u);
            f[u][0]+=f[v][1];
            f[u][1]+=min(f[v][0],f[v][1]);
        }
}
void dfs1(int u,int Fa)
{
    fa[u]=Fa;
    siz[u]=1;
    dep[u]=dep[Fa]+1;
    for(int i=head[u];i;i=nx[i])
        {
            int v=to[i];
            if(v==Fa)
                continue;
            dfs1(v,u);
            siz[u]+=siz[v];
            if(siz[v]>siz[mxson[u]])
                mxson[u]=v;
        }
}
void dfs2(int u,int tp)
{
    top[u]=tp;
    dfn[u]=++idx;
    rnk[idx]=u;
    if(mxson[u])
        dfs2(mxson[u],tp);
    for(int i=head[u];i;i=nx[i])
        {
            int v=to[i];
            if(v!=fa[u] && v!=mxson[u])
                dfs2(v,v);
        }
}
const ll inf=1e18;
struct node{
    ll v[2][2];
    int l,r;
    node(){v[0][0]=v[0][1]=v[1][0]=v[1][1]=inf;}
    node operator * (const node &rhs) const
        {
            node res;
            res.l=l,res.r=rhs.r;
            for(int k=0;k<2;++k)
                for(int i=0;i<2;++i)
                    for(int j=0;j<2;++j)
                        res.v[i][j]=min(res.v[i][j],v[i][k]+rhs.v[k][j]);
            return res;
        }
};
node val[MAXN];
struct SegTree{ 
    node Tree[MAXN<<2];
    #define root Tree[o]
    #define lson Tree[o<<1]
    #define rson Tree[o<<1|1]
    inline void pushup(int o)
        {
            root=lson*rson;
        }
    void BuildTree(int o,int l,int r)
        {
            root.l=l,root.r=r;
            if(l==r)
                {
                    int u=rnk[l],g[2];
                    g[0]=0,g[1]=w[u];
                    for(int i=head[u];i;i=nx[i])
                        {
                            int v=to[i];
                            if(v==fa[u] || v==mxson[u])
                                continue;
                            g[0]+=f[v][1];
                            g[1]+=min(f[v][0],f[v][1]);
                        }
                    root.v[0][0]=inf,root.v[0][1]=g[0];
                    root.v[1][0]=root.v[1][1]=g[1];
                    val[l]=root;
                    return;
                }
            int mid=(l+r)>>1;
            BuildTree(o<<1,l,mid);
            BuildTree(o<<1|1,mid+1,r);
            pushup(o);
        }
    void update(int o,int pos)
        {
            int l=root.l,r=root.r;
            if(l==r)
                {
                    root=val[pos];
                    return;
                }
            int mid=(l+r)>>1;
            if(pos<=mid)
                update(o<<1,pos);
            else
                update(o<<1|1,pos);
            pushup(o);
        }
    node query(int o,int L,int R)
        {
            int l=root.l,r=root.r;
            if(L<=l && r<=R)
                return root;
            int mid=(l+r)>>1;
            if(R<=mid)
                return query(o<<1,L,R);
            if(L>mid)
                return query(o<<1|1,L,R);
            return query(o<<1,L,R)*query(o<<1|1,L,R);
        }
}T;
node query(int x)
{
    return T.query(1,dfn[x],dfn[bot[x]]);
}
ll getans()
{
    node s=query(1);
    return min(s.v[0][1],s.v[1][1]);
}
void upd(int x,ll nv)
{
    val[dfn[x]].v[1][0]-=w[x]-nv;
    val[dfn[x]].v[1][1]-=w[x]-nv;
    w[x]=nv;
    while(x)
        {
            node org=query(top[x]);
            T.update(1,dfn[x]);
            node nx=query(top[x]);
            x=fa[top[x]];
            val[dfn[x]].v[0][1]+=nx.v[1][1]-org.v[1][1];
            val[dfn[x]].v[1][0]+=min(nx.v[1][1],nx.v[0][1])-min(org.v[1][1],org.v[0][1]);
            val[dfn[x]].v[1][1]=val[dfn[x]].v[1][0];
        }
}
void solve(int x1,int t1,int x2,int t2)
{
    if(t1==0 && t2==0 && (fa[x1]==x2 || fa[x2]==x1))
        return void(puts("-1"));
    ll v1=w[x1],v2=w[x2];
    upd(x1,v1+(t1?-inf:inf));
    upd(x2,v2+(t2?-inf:inf));
    ll res=getans();
    res+=t1?inf:0;
    res+=t2?inf:0;
    printf("%lld\n",res);
    upd(x1,v1);
    upd(x2,v2);
}
char tip[5];
signed main()
{
    //freopen("tx.in","r",stdin);
    n=read(),m=read();
    scanf("%s",tip);
    for(int i=1;i<=n;++i)
        w[i]=read();
    for(int i=1;i<n;++i)
        {
            int u=read(),v=read();
            addedge(u,v);
        }
    DP(1,0);
    dfs1(1,0);
    dfs2(1,1);
    for(int i=1;i<=n;++i)
        {
            if(i==top[i])
                {
                    int t=i;
                    while(mxson[t])
                        t=mxson[t];
                    bot[i]=t;
                }
        }
    T.BuildTree(1,1,n);
    while(m--)
        {
            int a=read(),b=read(),c=read(),d=read();
            solve(a,b,c,d);
        }
    return 0;
}

转载于:https://www.cnblogs.com/jklover/p/10466278.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值