【Codeforces 1178G】The Awesomest Vertex

分块+凸包

题目大意:

给定一棵有根树,每个点有权值 $a_i$ 和 $b_i$。

对于一个结点 $v$,记 $R(v)$ 表示 $v$ 的祖先集合,则 $v$ 的贡献为:

需要支持两个操作:

  1. 给定 $v,x$,把 $a_v$ 的值加上 $x$。$x$为正整数。
  2. 给定 $v$,问以 $v$ 为根的子树中,贡献最大的结点的贡献。

题解:

分块+凸包的套路题。虽然一眼秒了不过就是想写一遍啊

考虑记 $B_v=\left|\sum\limits_{w\in R(v)}b_w \right|$,这个值显然不会变。

再考虑令 $A_v=\sum\limits_{w\in R(v)}a_w$,然后我们直接维护 $A_v$ 的值。

考虑单点修改操作,会对 $v$ 所在的所有子树中的 $A$ 的值进行变化。

那么相当于一个子树修改子树查询问题,用 dfs 序转化为序列问题。

考虑将 $(B_v,A_v\times B_v)$ 当做二维平面上的一个点,并对所有 $A$ 整体加上$\Delta A$,考虑哪些点可能成为最大值。

不难发现,只有在上凸壳上面的点,才可能在 $A_v$ 加上某个值后成为答案。容易用叉积、斜率等方式证明。

考虑分块,对每个块维护凸包。那么对于全局修改,可以直接在块上打标记。询问整个块的答案的时候,直接在凸包上二分最大值即可。对于边角修改,暴力即可,修改完重构凸包。

观察到 $x$ 为正,那么 $\Delta A$ 始终变大,最大值在凸包上的位置单调向右移动。所以直接使用一个指针指向当前最大值,然后往右移动即可。边角修改的时候,把指针复原到最左边即可。复杂度均摊正确。

由于产生贡献的是 $|A_v|$,所以我们对 $A_v$ 和 $-A_v$ 分别维护凸包即可。维护负数的时候,由于 $\Delta A$ 单调变小,所以指针要从右往左移。

时间复杂度 $O(m\sqrt n)$。

Code:

#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<utility>
#include<vector>
using namespace std;
#define bel(x)((x-1)/siz+1)
const int N=2e5+5,siz=512,BL=N/siz+2;
typedef long long LL;
int n,q,fa[N],head[N],cnt,a[N],b[N],dfn[N],idx,sz[N],L[BL],R[BL],idfn[N];
int blocks;
int A[N],B[N];
struct edge{
    int to,nxt;
}e[N];
struct hull{
    struct point{
        int x;
        LL y;
        inline point operator-(const point&rhs)const{return(point){x-rhs.x,y-rhs.y};}
    };
    inline int cross(point a,point b){
        if(a.x*b.y==a.y*b.x)return 0;
        if(a.x*b.y>a.y*b.x)return 1;
        return -1;
    }
    vector<point>pt;
    int it;
    inline void clear(){pt.clear();}
    inline void insert(int x,LL y){
        point s=(point){x,y};
        while(pt.size()>1){
            if(cross(s-pt[pt.size()-2],pt[pt.size()-1]-pt[pt.size()-2])==1)break;else pt.pop_back();
        }
        pt.push_back(s);
    }
    inline LL getans(LL dlt,const int fh){
        while(it+fh>=0&&it+fh<pt.size()&&pt[it].x*dlt+pt[it].y<pt[it+fh].x*dlt+pt[it+fh].y)
            it+=fh;
        return pt[it].x*dlt+pt[it].y;
    }
};
struct BLOCK{
    pair<int,int>b[siz];
    int l,r,len;
    hull h1,h2;
    LL dlt;
    inline void build(){
        for(int i=1;i<=len;++i){
            h1.insert(b[i].first,b[i].first*(LL)A[b[i].second]);
            h2.insert(b[i].first,-b[i].first*(LL)A[b[i].second]);
        }
        h1.it=0,h2.it=h2.pt.size()-1;
    }
    inline void init(int L,int R){
        l=L,r=R,len=r-l+1;
        for(int i=1;i<=len;++i)
            b[i]=make_pair(B[L+i-1],L+i-1);
        sort(b+1,b+len+1);
        build();
    }
    void modify(int L,int R,int v){
        for(int i=L;i<=R;++i)
            A[i]+=v;
        h1.clear(),h2.clear();
        build();
    }
    inline LL ask(int L,int R){
        LL ret=0;
        for(int i=L;i<=R;++i)
            ret=max(ret,llabs((LL)(A[i]+dlt)*B[i]));
        return ret;
    }
    inline LL get(){
        return max(h1.getans(dlt,1),h2.getans(-dlt,-1));
    }
}G[BL];
void dfs(int now){
    idfn[dfn[now]=++idx]=now,sz[now]=1;
    a[now]+=a[fa[now]],b[now]+=b[fa[now]];
    for(int i=head[now];i;i=e[i].nxt)dfs(e[i].to),sz[now]+=sz[e[i].to];
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n>>q;
    for(int i=2;i<=n;++i){
        cin>>fa[i];
        e[++cnt]=(edge){i,head[fa[i]]},head[fa[i]]=cnt;
    }
    for(int i=1;i<=n;++i)cin>>a[i];
    for(int i=1;i<=n;++i)cin>>b[i];
    dfs(1);
    blocks=bel(n);
    for(int i=1;i<=blocks;++i)L[i]=R[i-1]+1,R[i]=i*siz;
    R[blocks]=n;
    for(int i=1;i<=n;++i)
        A[i]=a[idfn[i]],B[i]=abs(b[idfn[i]]);
    for(int i=1;i<=blocks;++i)G[i].init(L[i],R[i]);
    while(q--){
        int op;
        cin>>op;
        if(op==1){
            int v,x;
            cin>>v>>x;
            int l=dfn[v],r=dfn[v]+sz[v]-1;
            const int bL=bel(l),bR=bel(r);
            if(bL==bR)G[bL].modify(l,r,x);else{
                G[bL].modify(l,R[bL],x);
                G[bR].modify(L[bR],r,x);
                for(int i=bL+1;i<bR;++i)
                    G[i].dlt+=x;
            }
        }else{
            int v;
            cin>>v;
            int l=dfn[v],r=dfn[v]+sz[v]-1;
            const int bL=bel(l),bR=bel(r);
            LL ans=0;
            if(bL==bR)
                ans=G[bL].ask(l,r);
            else{
                ans=max(ans,max(G[bL].ask(l,R[bL]),G[bR].ask(L[bR],r)));
                for(int i=bL+1;i<bR;++i)
                    ans=max(ans,G[i].get());
            }
            cout<<ans<<'\n';
        }
    }
    return 0;
}