【JZOJ6232】【20190625】喜欢最最痛

题目

\(n\)个节点的树,边权为正整数。

从1 号点开始走一个路径并最终回到 1 号点,且这条路径经过了所有的边。

一条路径的代价就是它经过的边的边权之和。

可以加若干条额外边,第 i 条加的额外边的边权为 正整数\(A_ i\)

注意,不一定要经过所有的额外边

对于所有的$ K \in [0, m]$,你需要输出允许加 \(\le K\) 条额外边的最小路径代价。

题解

  • 假设我们选了\(x\)条额外边,答案分为两部分:

    1.在前\(K\)条边里选\(x\)条边,使得长度和最小

    2.在原树上选\(x\)条边不相交的路径使得长度和最大

  • 考虑2,每次确定当前树的直径,将直径上的边全部取反,做\(x\)次即可

    具体用LCT维护ddp

    注意到ddp的话区间取反和翻转无法下放,但只有这两种操作

    可以预处理出splay节点取反,翻转的信息,修改的时候直接调用

  • 由于12都是凸的,所以我们直接三分求答案

  • 时间复杂度:\(O(n \ log ^2 n + n \ log \ n * LCT )\)  

Code

没写出来..先放YFZ的std:

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
typedef pair<ll,int> pii;
const ll inf=1e18;
int n,fa[maxn],K,C,W;
ll ans,sum,val[maxn],anses[maxn];
pii sval[maxn];
vector<pii>G[maxn];

struct pxx{
    ll u,v,w;
    pxx(){}
    pxx(ll u,ll v,ll w):u(u),v(v),w(w){}
    pxx operator+(const ll x)const{return pxx(u,v,w+x);}
    pxx operator-(const ll x)const{return pxx(u,v,w-x);}
    pxx operator+(const pxx& x)const{
        pxx ret;
        ret.u=u;ret.v=x.u;
        if(ret.u<ret.v)swap(ret.u,ret.v);
        ret.w=w+x.w;
        return ret;
    }
    int operator<(const pxx& d)const{
        if(w!=d.w)return w<d.w;
        if(u!=d.u)return u<d.u;
        return v<d.v;
    }
}; 
//定义一条链的结构体
struct data{
    pxx pre,suf,ans;ll sum;
    data(){}
    data(pxx pre,pxx suf,pxx ans,ll sum):pre(pre),suf(suf),ans(ans),sum(sum){}
    data operator+(const data& d)const{
        data ret;
        ret.pre=max(pre,d.pre+sum);
        ret.suf=max(suf+d.sum,d.suf);
        ret.ans=max(ans,d.ans);
        ret.ans=max(ret.ans,suf+d.pre);
        ret.sum=sum+d.sum;
        return ret;
    }
};
//定义一个节点的信息,类似于最大子段和
namespace LCT{
    int ch[maxn][2],fa[maxn],tg[maxn],ftg[maxn];
    ll val[maxn];
    multiset<pxx> st[maxn],stans[maxn];
    data dp[maxn];
    pxx cpre[maxn],cans[maxn];
    data a[2][maxn],ra[2][maxn];
    void clr(int n){
        for(int i=1;i<=n;++i){
            ch[i][0]=ch[i][1]=fa[i]=tg[i]=ftg[i]=val[i]=0;
            st[i].clear(),stans[i].clear();
        }   
        memset(dp,0,sizeof(data)*n);
        memset(cpre,0,sizeof(pxx)*n);
        memset(cans,0,sizeof(pxx)*n);
        for(int i=0;i<2;++i){
            memset(a[i],0,sizeof(data)*n);
            memset(ra[i],0,sizeof(data)*n);
        }
    }
    //
    void upd(int o){
        int ls=ch[o][0],rs=ch[o][1];
        if(o>n){
            a[0][o]=ra[0][o]=data(dp[o].pre+val[o],dp[o].pre+val[o],dp[o].ans,val[o]);
            a[1][o]=ra[1][o]=data(dp[o].pre-val[o],dp[o].pre-val[o],dp[o].ans,-val[o]);
        } else {
            a[1][o]=ra[1][o]=
            a[0][o]=ra[0][o]=data(dp[o].pre,dp[o].pre,max(dp[o].pre+pxx(o,0,0),dp[o].ans),0);
        }
        if(ls){
            for(int i=0;i<2;++i){
                a[i][o]=a[i][ls]+a[i][o];
                ra[i][o]=ra[i][o]+ra[i][ls];    
            }
        }
        if(rs){
            for(int i=0;i<2;++i){
                a[i][o]=a[i][o]+a[i][rs];
                ra[i][o]=ra[i][rs]+ra[i][o];    
            }
        }
    }
    //
    void _pd(int o,int t,int ft){
        if(ft){
            ftg[o]^=1;
            swap(a[0][o],a[1][o]);
            swap(ra[0][o],ra[1][o]);
            val[o]=-val[o];
        }
        if(t){
            tg[o]^=1;
            swap(ch[o][0],ch[o][1]);
            swap(a[0][o],ra[0][o]);
            swap(a[1][o],ra[1][o]);
        }
    }
    //修改
    void pd(int o){
        if(ch[o][0])_pd(ch[o][0],tg[o],ftg[o]);
        if(ch[o][1])_pd(ch[o][1],tg[o],ftg[o]);
        tg[o]=ftg[o]=0;
    }//下放
    bool isroot(int x){
        if(!x||!fa[x])return true;
        return ch[fa[x]][1]!=x&&ch[fa[x]][0]!=x;
    }
    void rotate(int p){
        int q=fa[p],y=fa[q],k=(ch[q][1]==p);
        if(!isroot(q))ch[y][ch[y][1]==q]=p;
        fa[ch[q][k]=ch[p][k^1]]=q;
        fa[ch[p][k^1]=q]=p,fa[p]=y;
        upd(q);
    }
    void splay(int x){
        int y;
        while(!isroot(x)){
            pd(fa[y=fa[x]]),pd(y),pd(x);
            if(!isroot(y)){
                if((ch[fa[y]][1]==y)^(ch[y][1]==x))rotate(x);
                else rotate(y); 
            }
            rotate(x);
        }
        pd(x),upd(x);
    }
    //
    void uupd(int x){
        if(x<=n){
            dp[x].pre=*st[x].rbegin();
            dp[x].ans=*stans[x].rbegin();
            if(st[x].size()>=2){
                auto it=st[x].end();
                dp[x].ans=max(dp[x].ans,*prev(it)+*prev(prev(it)));
            }
        } else {
            dp[x].pre=st[x].size()?*st[x].rbegin():pxx(0,0,-inf);
            dp[x].ans=stans[x].size()?*stans[x].rbegin():pxx(0,0,-inf);
            if(st[x].size()>=2){
                auto it=st[x].end();
                dp[x].ans=max(dp[x].ans,*prev(it)+*prev(prev(it))+val[x]);
            }
        }
    }
    //修改一个点的轻链信息
    void cadd(int x,int y){
    //  printf("cadd:(%d)<%lld,%lld,%lld>\n",y,a[0][y].ans.u,a[0][y].ans.v,a[0][y].ans.w);
        st[x].insert(a[0][y].pre);
        stans[x].insert(a[0][y].ans);
        uupd(x);
    }
    //加入一个轻链信息
    void del(int x,int y){
    //  printf("del:(%d)[%lld,%lld,%lld]\n",y,a[0][y].ans.u,a[0][y].ans.v,a[0][y].ans.w);
        st[x].erase(a[0][y].pre);
        stans[x].erase(a[0][y].ans);
        uupd(x);
    }
    //删除一个轻链信息
    void access(int x){
        for(int y=0;x;y=x,x=fa[x]){
            splay(x);
        //  printf("{%d,%d}",x,y);
            if(ch[x][1])cadd(x,ch[x][1]);
            if(y)del(x,y);
            ch[x][1]=y;
            upd(x);
        }
    }
    
    void rever(int x){
        access(x),splay(x);
        _pd(x,1,0),pd(x);
    }
    
    void link(int u,int v){
        access(u),splay(u);
        rever(v);
        fa[v]=u;
        cadd(u,v);
        upd(u);
    }
    void mdy(int u,int v){
        rever(u),access(v),splay(v);
        _pd(v,0,1),pd(v);
    }
    void dfs(int u){
        pd(u);
        if(ch[u][0])dfs(ch[u][0]);
        printf("[%d[%d,%d](%lld,%lld,%lld,%d)]",u,ch[u][0],ch[u][1],dp[u].pre.w,a[0][u].ans.w,a[1][u].ans.w,a[1][u].sum);
        if(ch[u][1])dfs(ch[u][1]);
    }
};
int ptr=0;
void dfs(int u,int f){
    for(auto v:G[u])if(v.first!=f){
        dfs(v.first,u);
        LCT::fa[v.first]=v.second;
        LCT::cadd(v.second,v.first);
        LCT::fa[v.second]=u;
        LCT::upd(v.second);
        LCT::cadd(u,v.second);
        LCT::upd(u);
    }
    LCT::upd(u);
}
//?
ll trsum[maxn],trcnt[maxn];
ll calzzz(ll x){
    ll nw=0,ans=0,nwcnt=0;
    for(int i=21;i>=0;--i){
        nw+=(1<<i);
        if(nw>K||trcnt[nw]+nwcnt>x)nw-=(1<<i);
        else nwcnt+=trcnt[nw],ans+=trsum[nw];
    }
    return ans+anses[x];
}
//BIT维护1
ll solans(ll K){
    int l=0,r=K-1;
    ll ans=calzzz(K);
    while(l<=r){
        int mid=l+r>>1;
        if(calzzz(mid)>calzzz(mid+1))l=mid+1,ans=min(ans,calzzz(mid+1));
        else r=mid-1,ans=min(ans,calzzz(mid));
    }
    return ans;
}
//二分斜率求答案
int main(){
    freopen("love.in","r",stdin);
    freopen("love.out","w",stdout);
    scanf("%d%d",&n,&K);
    sum=0;
    ptr=n;
    LCT::clr(2*n);
    for(int i=1;i<=n;++i)G[i].clear();
    for(int i=1;i<=n;++i){
        LCT::st[i].insert(pxx(i,0,0));
        LCT::stans[i].insert(pxx(i,i,0));
        LCT::uupd(i),LCT::upd(i);
    }
    for(int i=2,u,v,w;i<=n;++i){
        scanf("%d%d%d",&u,&v,&w);
        ++ptr;
        LCT::val[ptr]=w;
        LCT::uupd(ptr),LCT::upd(ptr);
        
        G[u].push_back(pii(v,ptr));
        G[v].push_back(pii(u,ptr));
        sum+=2*w;
    }
    dfs(1,0);
    for(int i=1;i<=K;++i)scanf("%lld",&val[i]),sval[i]=pii(val[i],i);
    sort(sval+1,sval+K+1);
    int flg=0;
    anses[0]=sum;
    printf("%lld ",solans(0));
    for(int i=1;i<=K;++i){
        int R=1;
        LCT::rever(R);
        pxx path=LCT::a[0][R].ans;
        sum-=path.w;
        anses[i]=sum;
        if(path.w==0&&!flg){
            flg=1;
            fprintf(stderr,"[%d]",i);
        }
        for(int x=lower_bound(sval+1,sval+K+1,pii(val[i],i))-sval;x<=K;x+=x&-x)
            trsum[x]+=val[i],trcnt[x]++;
        int u=path.u,v=path.v;
        LCT::mdy(u,v);
        printf("%lld ",solans(i));
    }
}

转载于:https://www.cnblogs.com/Paul-Guderian/p/11112095.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值