UOJ418. 【集训队作业2018】三角形

http://uoj.ac/problem/418

题解

考虑激活每个节点时,它的每个儿子都是放满的。

那每一次的操作我们可以用一个二元组来表示\((w_i-\sum w_{son},\sum w_{son})\)

表示这一次操作完后的增量和这次操作中石子数达到的峰值。

那么一个节点被操作当且仅当它的所有儿子都被操作。

这样很不好处理,所以我们把整个操作序列倒过来,这样限制一个点的只有他的父亲。

那么二元组会变成:\((\sum w_{son}-w_i,\sum w_{son})\),我们还是需要最小化这个序列的历史最值,因为按照这个序列模拟的话是可以还原出它的每一个时刻的。

然后考虑贪心,我们需要对每一个操作求出它的优先度。

对于\(x<0\)的操作,它显然放在前面会更优,如果有多个\(<0\)的操作,\(y\)更小的放在前面。

对于\(x\geq 0\)的操作,我们要让(x,y)优于(dx,dy),那么就有:

\[max(y,dy+x)<max(dy,dx+y) \]

前面的常量先不管。

\[dy+x<dx+y ->x-y>dx-dy \]

于是我们弄清楚了每个元素的优先度,然后我们求出全局的一个最优序列。

每次找到最优的,如果这个点还没有被父亲激活,那么就和父亲合并。

通过观察我们发现每棵子树的最优序列是全局的子序列,然后我们可以线段树合并求答案。

代码

#include<bits/stdc++.h>
#define N 200002
#define ls tr[cnt].l
#define rs tr[cnt].r
using namespace std;
typedef long long ll;
int f[N],n,id[N],fa[N],T[N],tott,nxt[N];
bool vis[N];
ll w[N],s[N],ans[N],val[N];
inline ll rd(){
    ll x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
vector<int>vec[N];
int find(int x){return f[x]=f[x]==x?x:find(f[x]);}
struct node{
    ll x,y;
    int h,t;
    inline node operator +(const node &b)const{return node{x+b.x,max(y,x+b.y),h,b.t};}
    inline bool operator <(const node &b)const{
        int o1=x>=0,o2=b.x>=0;
        if(o1!=o2)return x<b.x;
        if(!o1){
            if(y!=b.y)return y<b.y;
            return h<b.h;
        }
        if(y-x!=b.y-b.x)return y-x>b.y-b.x;
        return h<b.h;
    }
};
struct seg{
    node x;
    int l,r;
}tr[N*23];
node pre[N];
set<node>q;
int merge(int u,int v){
    if(!u||!v)return u|v;
    tr[u].l=merge(tr[u].l,tr[v].l);
    tr[u].r=merge(tr[u].r,tr[v].r);
    tr[u].x=tr[tr[u].l].x+tr[tr[u].r].x;
    return u;
}
void ins(int &cnt,int l,int r,int x,int id){
    cnt=++tott;
    if(l==r){
      tr[cnt].x=node{val[id],s[id],l,l};
      return;
    }
    int mid=(l+r)>>1;
    if(x<=mid)ins(ls,l,mid,x,id);
    else ins(rs,mid+1,r,x,id);
    tr[cnt].x=tr[ls].x+tr[rs].x;
}
void dfs(int u,int fa){
    ins(T[u],1,n,id[u],u);
    for(vector<int>::iterator it=vec[u].begin();it!=vec[u].end();++it){
        int v=*it;
        dfs(v,u);
        T[u]=merge(T[u],T[v]);
    }
    ans[u]=tr[T[u]].x.y+w[u];
}
int main(){
    int TT=rd();
    n=rd();
    for(int i=2;i<=n;++i)fa[i]=rd(),vec[fa[i]].push_back(i);
    for(int i=1;i<=n;++i)w[i]=rd(),s[fa[i]]+=w[i],f[i]=i;
    for(int i=1;i<=n;++i){
        val[i]=s[i]-w[i];
        pre[i]=node{val[i],s[i],i,i};
        q.insert(pre[i]);
    }
    vis[0]=1;
    int now=0;
    while(!q.empty()){
        set<node>::iterator it=q.begin();
        node x=*it;
        q.erase(it);
        if(vis[fa[x.h]]){
            nxt[now]=x.h;
            while(now!=x.t)vis[now]=1,now=nxt[now];
            vis[now]=1;
        }
        else{
            int pr=find(fa[x.h]);
            q.erase(pre[pr]);
            nxt[pre[pr].t]=pre[x.h].h;
            pre[pr]=pre[pr]+pre[x.h];
            f[find(x.h)]=pr;
            q.insert(pre[pr]);
        }
    }
    int cnt=0;
    now=0;
    for(int i=1;i<=n;++i){
        cnt++;
        now=nxt[now];
        id[now]=i;
    }
    dfs(1,0);
    for(int i=1;i<=n;++i)printf("%lld ",ans[i]);
    return 0;
}
posted @ 2019-06-10 20:32  comld  阅读(666)  评论(0编辑  收藏  举报