bzoj 4712 洪水 - 动态dp - 树剖 - 线段树 - 矩阵乘法

121 篇文章 0 订阅
52 篇文章 0 订阅

动态dp入门题,转移有结合律可以写成矩阵的形式,特判叶子。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
#define gc getchar()
#define lint long long
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
#define N 200010
#define INF (LLONG_MAX/10-10)
using namespace std;
inline int inn()
{
    int x,ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
inline lint inln()
{
    lint x;int ch;while((ch=gc)<'0'||ch>'9');
    x=ch^'0';while((ch=gc)>='0'&&ch<='9')
        x=(x<<1)+(x<<3)+(ch^'0');return x;
}
struct edges{
    int to,pre;
}e[N<<1];int h[N],etop,dfs_clock,L[N],tms[N],top[N];
lint a[N],s[N],dp[N];int lst[N],bot[N],sz[N],son[N],fa[N];
inline int add_edge(int u,int v) { return e[++etop].to=v,e[etop].pre=h[u],h[u]=etop; }
int fir_dfs(int x,int f=0)
{
    fa[x]=f,sz[x]=1,son[x]=0;
    for(int i=h[x],y;i;i=e[i].pre)
        if((y=e[i].to)^fa[x])
        {
            sz[x]+=fir_dfs(y,x);
            if(sz[y]>sz[son[x]]) son[x]=y;
        }
    return sz[x];
}
int sec_dfs(int x)
{
    bot[x]=x,tms[L[x]=++dfs_clock]=x,s[x]=0ll;
    if(son[x]) top[son[x]]=top[x],sec_dfs(son[x]),bot[x]=bot[son[x]];
    for(int i=h[x],y;i;i=e[i].pre)
        if((y=e[i].to)!=fa[x]&&e[i].to!=son[x]) top[y]=y,sec_dfs(y),s[x]+=dp[y];
    return (son[x]?0:s[x]=INF),dp[x]=min(a[x],s[x]+dp[son[x]]),0;
}
struct node{
    lint a,b,c,d;bool ept;
    node(lint _a=0,lint _b=0)
    {   a=_a,b=_b,c=INF,d=0,ept=0;  }
    inline int init() { return ept=1; }
    inline node operator=(const node &n)
    {   return a=n.a,b=n.b,c=n.c,d=n.d,ept=n.ept,*this; }
    inline node operator+(const node &n)const
    {
        if(ept) return n;if(n.ept) return *this;node r;
        r.a=min(a+n.a,b+n.c),r.a=min(r.a,INF),
        r.b=min(a+n.b,b+n.d),r.b=min(r.b,INF);
//      r.c=min(c+n.a,d+n.c),r.c=min(r.c,INF),
//      r.d=min(c+n.b,d+n.d),r.d=min(r.d,INF);
        return r;
    }
    inline node operator+=(const node &n)
    {   return (*this)=(*this)+n;   }
    inline lint dp() { return min(a,b); }
    inline int show()const
    {
        debug(a)sp,debug(b)sp,debug(c)sp,debug(d)ln;return 0;
    }
}ans;
struct segment{
    int l,r;node v;
    lint s;segment *ch[2];
}*rt;
inline int push_up(segment* &rt) { return rt->v=rt->ch[0]->v+rt->ch[1]->v,0; }
int build(segment* &rt,int l,int r)
{
    rt=new segment,rt->l=l,rt->r=r;int mid=(l+r)>>1;
    if(l==r) return rt->v=node(rt->s=s[tms[l]],a[tms[r]]),0;
    return build(rt->ch[0],l,mid),build(rt->ch[1],mid+1,r),push_up(rt),0;
}
int update(segment* &rt,int p,lint ds,lint da)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(l==r) return rt->v.a+=ds,rt->v.b+=da,0;
    return update(rt->ch[p>mid],p,ds,da),push_up(rt);
}
int query(segment* &rt,int s,int t)
{
    int l=rt->l,r=rt->r,mid=(l+r)>>1;
    if(s<=l&&r<=t) return ans+=rt->v,0;
    if(s<=mid) query(rt->ch[0],s,t);
    if(mid<t) query(rt->ch[1],s,t);
    return 0;
}
inline int update(int x,lint v)
{
    int c=0;
    for(int y=x;fa[top[y]];y=fa[top[y]]) lst[++c]=top[y];
    for(int i=c,y,z;i;i--) y=lst[i],z=fa[y],
        ans.init(),query(rt,L[y],L[bot[y]]),
        update(rt,L[z],-ans.dp(),0);
    update(rt,L[x],0,v);
    for(int i=1,y,z;i<=c;i++) y=lst[i],z=fa[y],
        ans.init(),query(rt,L[y],L[bot[y]]),
        update(rt,L[z],ans.dp(),0);
    return 0;
}
int main()
{
    int n=inn();
    for(int i=1;i<=n;i++) a[i]=inln();
    for(int i=1,u,v;i<n;i++)
        u=inn(),v=inn(),add_edge(u,v),add_edge(v,u);
    fir_dfs(1),top[1]=1,sec_dfs(1),build(rt,1,n);
//  for(int i=1;i<=n;i++) debug(i)sp,debug(fa[i])sp,debug(son[i])sp,debug(top[i])sp,debug(bot[i])sp,debug(L[i])sp,debug(tms[i])ln;
//  for(int i=1;i<=n;i++) debug(i)sp,debug(a[i])sp,debug(s[i])sp,debug(dp[i])ln;
    for(int T=inn();T;T--)
    {
        int ch,x;lint y;while((ch=gc)!='Q'&&ch!='C'&&ch!='q'&&ch!='c');
        if(ch=='C'||ch=='c') x=inn(),y=inln(),update(x,y);
        else x=inn(),ans.init(),query(rt,L[x],L[bot[x]]),printf("%lld\n",ans.dp());
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值