• [CF1303G] Sum of Prefix Sums


    给定一棵 (n) 个点的带点权的树,求树上的路径 (x_1,...,x_k) ,最大化 (sum_{i=1}^k ia_{x_i})

    Solution

    树上路径问题可用点分治。

    考虑如何合并两条路径对每条路径,记 (l) 为长度(点数),(v)(sum_{i=1}^l ia_{x_i})(s)(sum a_i) ,那么对于两条路径 ((l_1,v_1,s_1),(l_2,v_2,s_2)),它们的并为 ((l_1+l_2-1,v_1+s_1l_2+v_2,s_1+s_2))

    于是答案为 (v_1 + v_2 + s_1l_2),其中 (_1) 是和之前扫描过的有关的信息,(_2) 是和当前正在扫描的位置有关的信息

    (之所以这样取的原因是,之后李超线段树中我们需要控制下标的区间,而 (l) 的范围是较小的)

    对于某一次查询来说,(v_2) 是定值,于是查询只有一个参数 (l_2) ,所以现在我们要最大化 (l_2s_1 + v_1),不妨转化为

    • 插入一条直线 (y=ax+b),其中 (a=s_1,b=v_1)
    • 询问 (x=l_2) 与所有直线交点中最上面的那一个

    标准的李超线段树模板

    我又把点分敲错,数组开小

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    
    const bool dbg = 0;
    
    namespace seg {
    const int N = 4e6+5;
    #define lc x<<1
    #define rc x<<1|1
    #define mid ((l+r)>>1)
    struct Node {
        int a,b;
    } tree[N];
    void ins(int x,int l,int r,int a,int b) {
        if(tree[x].a*l+tree[x].b<=a*l+b&&tree[x].a*r+tree[x].b<=a*r+b)  {
            tree[x].a=a,tree[x].b=b;
        }
        else {
            if(tree[x].a*l+tree[x].b>=a*l+b&&tree[x].a*r+tree[x].b>=a*r+b) return;
            if(tree[x].a*mid+tree[x].b<a*mid+b)swap(tree[x].a,a),swap(tree[x].b,b);
            if(a<tree[x].a)ins(lc,l,mid,a,b);
            else ins(rc,mid+1,r,a,b);
        }
    }
    void build(int x,int l,int r) {
        tree[x].a=tree[x].b=0;
        if(l==r)return;
        build(lc,l,mid);
        build(rc,mid+1,r);
    }
    int query(int x,int l,int r,int p) {
        int ans=tree[x].a*p+tree[x].b;
        if(l==r)return ans;
        if(p<=mid)ans=max(ans,query(lc,l,mid,p));
        else ans=max(ans,query(rc,mid+1,r,p));
        return ans;
    }
    };
    
    int tot;
    const int N = 300005;
    int n,val[N],siz[N],vis[N],mx[N],fa[N],l[N],v[N],s[N],sz,ans;
    vector <int> g[N],sta;
    
    void dfs(int p) {
        siz[p]=1;
        mx[p]=0; //!!!
        sta.push_back(p);
        for(int q:g[p]) if(fa[p]!=q && !vis[q]) {
            fa[q]=p;
            dfs(q);
            siz[p]+=siz[q];
            mx[p]=max(mx[p],siz[q]);
        }
    }
    
    void dfs1(int p) {
        if(dbg) cout<<"   dfs1 "<<p<<" "<<l[p]<<" "<<v[p]<<" "<<s[p]<<endl;
        seg::ins(1,1,sz,s[p],v[p]);
        for(int q:g[p]) if(fa[p]!=q && !vis[q]) {
            l[q]=l[p]+1;
            s[q]=s[p]+val[q];
            v[q]=v[p]+l[q]*val[q];
            dfs1(q);
        }
    }
    
    void dfs2(int p) {
        if(dbg) cout<<"   dfs2 "<<p<<" "<<l[p]<<" "<<v[p]<<" "<<s[p]<<endl;
        ans=max(ans,seg::query(1,1,sz,l[p])+v[p]);
        if(dbg) cout<<"    ans="<<ans<<endl;
        for(int q:g[p]) if(fa[p]!=q && !vis[q]) {
            l[q]=l[p]+1;
            s[q]=s[p]+val[q];
            v[q]=v[p]+s[q];
            dfs2(q);
        }
    }
    
    
    void solve(int p) {
        fa[p]=0;
        sta.clear();
        dfs(p);
        for(int q:sta) mx[q]=max(mx[q],(int)sta.size()-siz[q]);
        int r=0,mv=1e9;
        for(int q:sta) if(mv>mx[q]) r=q, mv=mx[q];
        vis[r]=1;
        for(int q:sta) fa[q]=0;
        dfs(r); //!!!
        if(dbg) cout<<"solve "<<r<<endl;
        sz=sta.size();
        if(dbg) cout<<" round I"<<endl;
        seg::build(1,1,sz);
        l[r]=1; s[r]=val[r]; v[r]=val[r];
        if(dbg) cout<<" "<<l[r]<<" "<<s[r]<<" "<<v[r]<<endl;
        seg::ins(1,1,sz,s[r],v[r]);
        for(int i=0;i<g[r].size();i++) {
            int q=g[r][i];
            if(vis[q]) continue;
            if(dbg) cout<<"  subtree "<<q<<endl;
            l[q]=1;
            v[q]=val[q];
            s[q]=val[q];
            dfs2(q);
            l[q]=2;
            v[q]=val[r]+val[q]*2;
            s[q]=val[r]+val[q];
            dfs1(q);
        }
        ans=max(ans,seg::query(1,1,sz,0));
        if(dbg) cout<<"    ans="<<ans<<endl;
        seg::build(1,1,sz);
        if(dbg) cout<<" round II"<<endl;
        for(int i=g[r].size()-1;i>=0;--i) {
            int q=g[r][i];
            if(vis[q]) continue;
            if(dbg) cout<<"  subtree "<<q<<endl;
            l[q]=1;
            v[q]=val[q];
            s[q]=val[q];
            dfs2(q);
            l[q]=2;
            v[q]=val[r]+val[q]*2;
            s[q]=val[r]+val[q];
            dfs1(q);
        }
        for(int q:g[r]) if(!vis[q]) solve(q);
    }
    
    signed main() {
        //freopen("input.txt","r",stdin);
        //freopen("output.txt","w",stdout);
        //ios::sync_with_stdio(false);
        cin>>n;
        for(int i=1;i<n;i++) {
            int t1,t2;
            cin>>t1>>t2;
            g[t1].push_back(t2);
            g[t2].push_back(t1);
        }
        for(int i=1;i<=n;i++) cin>>val[i];
        solve(1);
        cout<<ans<<endl;
    }
    
    

    一杯茶一包烟,一个破题调一天

  • 相关阅读:
    【转】商业内幕(Business Insider)网站近期评出了全美20家最具创新力的科技创业公司
    【转】SQL Server如何截断(Truncate)和收缩(Shrink)事务日志
    【转】xp_dirtree储存过程漏洞
    【转】锻炼胸肌
    【转】sql server 版本中r2解释
    【转】生活感悟
    jquery插件学习之元素顶部悬浮
    html5学习
    javascript 学习之自定义滚动条加滚轮事件
    PHP连接数据库的方法
  • 原文地址:https://www.cnblogs.com/mollnn/p/12353915.html
Copyright © 2020-2023  润新知