[集训队作业2018]UOJ 418 三角形 - 线段树合并 - 堆 - 并查集

题目大意:给一棵树,每次可以选择拿走某个点的所有石子(放到手上),或者在任意一个满足其儿子节点都已经有石子的点 x x x放恰好 w x w_x wx个石子。对每个点 x x x求恰好在这个位置放 w x w_x wx个石子,一开始手上要有多少石子? n ≤ 1 0 5 n\le10^5 n105
题解:
考虑对每个子树怎么暴力求;
考虑将问题反过来,每次变为若一个点有石子,就在其儿子节点都放上石子,然后把这个点的石子取走。一开始只有根节点有恰好 w [ r t ] w[rt] w[rt]个石子。
那么考虑一个贪心,我们对每个点维护一个二元组 ( a , b ) (a,b) (a,b),一开始是 ( − w [ x ] + ∑ y ∈ s o n [ x ] w y , ∑ y ∈ s o n [ x ] w y ) (-w[x]+\sum_{y\in son[x]}w_y,\sum_{y\in son[x]}w_y) (w[x]+yson[x]wy,yson[x]wy),表示对这个点执行操作会导致目前石子数发生变化的量,和在这个过程中历史增量的最大值。
那么显然 ( a , b )   o p e r a t o r   ( c , d ) = ( a + c , m a x ( b , a + d ) ) (a,b)\mathrm{\ operator\ }(c,d)=(a+c,max(b,a+d)) (a,b) operator (c,d)=(a+c,max(b,a+d))
那么我们发现,对于两个二元组 ( a 1 , b 1 ) , ( a 2 , b 2 ) (a_1,b_1),(a_2,b_2) (a1,b1),(a2,b2)(假设现在石子数发生的变化量是 t o t tot tot):那么先操作1在操作2,历史增量最大值要对 max ⁡ ( b 1 , a 1 + b 2 ) \max(b_1,a_1+b_2) max(b1,a1+b2)取max,否则对 max ⁡ ( b 2 , a 2 + b 1 ) \max(b_2,a_2+b_1) max(b2,a2+b1)取max。
因此显然:
1)当 a 1 a_1 a1 a 2 a_2 a2 < 0 <0 <0时, b 1 , b 2 b_1,b_2 b1,b2才有可能取到max,因此 b b b更小的应当早操作。
2)否则若 a 1 a_1 a1 a 2 a_2 a2一个负一个非负,那么非负的那个优先;
3)否则若 a 1 + b 2 < a 2 + b 1 a_1+b_2<a_2+b_1 a1+b2<a2+b1,则1优先;否则2优先。(等价于 a − b a-b ab小的优先)。
那么每次我们操作一个点 x x x,若其到根的路径上还有没被操作的点,假设深度最大的是 y y y,那么操作完 y y y之后就要立刻操作 x x x,那么就让 v a l [ y ] + = v a l [ x ] val[y]+=val[x] val[y]+=val[x];否则操作 x x x
要把操作序列搞出来,可以用一个可删除堆加上 一个链表加上一个并查集处理出来。
最后发现,一个子树的操作序列,是整棵树的操作序列的子序列,因此搞一个线段树合并即可。

#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define Rep(i,v) rep(i,0,(int)v.size()-1)
#define lint long long
#define ull unsigned lint
#define db long double
#define pb push_back
#define mp make_pair
#define fir first
#define sec second
#define gc getchar()
#define debug(x) cerr<<#x<<"="<
#define sp <<" "
#define ln <
using namespace std;
typedef pair<int,int> pii;
typedef set<int>::iterator sit;
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;
}
const int N=200010;
int n,id[N],w[N],fa[N];vector<int> g[N];lint ans[N];
int R[N],tl[N];
struct UF{
    int n,fa[N];
    inline int init(int _n) { n=_n;rep(i,1,n) fa[i]=i;return 0; }
    inline int findf(int x) { return x==fa[x]?x:fa[x]=findf(fa[x]); }
    inline int merge(int x,int y) { return x=findf(x),y=findf(y),fa[x]=y; }
}uf;
struct E{
    lint a,b;E(lint _a=0,lint _b=0) { a=_a,b=_b; }
    inline E operator+(const E &n)const { return E(a+n.a,max(b,a+n.b)); }
    inline E &operator+=(const E &n) { return (*this)=(*this)+n,*this; }
    inline bool cmp(const E &n)const
    {
        if(a<0&&n.a<0) return b<n.b;
        if(a>=0&&n.a>=0) return a-b<n.a-n.b;
        return a<0;
    }
    inline bool operator<(const E &n)const { return n.cmp(*this); }
    inline int show()const { return cerr<<"("<<a<<", "<<b<<")\n",0; }
}val[N],vs[N];
struct MyHeap{
    priority_queue<pair<E,int> > q;
    map<pair<E,int>,int> d;
    MyHeap() { while(!q.empty()) q.pop();d.clear(); }
    inline int insert(pair<E,int> x) { return q.push(x),0; }
    inline int del(pair<E,int> x) { return d[x]++; }
    inline int top()
    {
        while(d[q.top()]) d[q.top()]--,q.pop();
        return q.top().sec;
    }
}hp;
namespace SEGMENT_space{
    struct segment{
        E val;segment *ch[2];
    }*T[N];
    inline int build(segment* &rt,int l,int r,int p,const E &v)
    {
        rt=new segment,rt->ch[0]=rt->ch[1]=NULL,rt->val=v;
        int mid=(l+r)>>1;if(l==r) return 0;
        if(p<=mid) build(rt->ch[0],l,mid,p,v);
        else build(rt->ch[1],mid+1,r,p,v);return 0;
    }
    inline int push_up(segment* &rt)
    {
        rt->val=E(0,0);
        if(rt->ch[0]!=NULL) rt->val+=rt->ch[0]->val;
        if(rt->ch[1]!=NULL) rt->val+=rt->ch[1]->val;
        return 0;
    }
    inline segment *_merge(segment* &x,segment* &y)
    {
        if(x==NULL) return y;if(y==NULL) return x;
        x->ch[0]=_merge(x->ch[0],y->ch[0]);
        x->ch[1]=_merge(x->ch[1],y->ch[1]);
        return push_up(x),x;
    }
}using SEGMENT_space::T;
using SEGMENT_space::build;
using SEGMENT_space::_merge;
inline int dfs(int x)
{
    build(T[x],1,n,id[x],vs[x]);
    Rep(i,g[x]) dfs(g[x][i]);
    Rep(i,g[x]) T[x]=_merge(T[x],T[g[x][i]]);
    return ans[x]=w[x]+T[x]->val.b,0;
}
int main()
{
    inn(),n=inn(),uf.init(n);
    rep(i,2,n) g[fa[i]=inn()].pb(i);
    rep(i,1,n) w[i]=inn();
    rep(x,1,n) Rep(i,g[x]) val[x].b+=w[g[x][i]];
    rep(x,1,n) val[x].a=val[x].b-w[x],vs[x]=val[x];
    rep(i,1,n) tl[i]=i,hp.insert(mp(val[i],i));int cnt=0;
    rep(i,1,n)
    {
        int x=hp.top(),f=uf.findf(fa[x]);hp.del(mp(val[x],x));uf.merge(x,f);
        if(!f) for(int y=x;y;y=R[y]) id[y]=++cnt;
        else tl[f][R]=x,tl[f]=tl[x],hp.del(mp(val[f],f)),hp.insert(mp(val[f]+=val[x],f));
    }
    dfs(1);rep(i,1,n) printf("%lld ",ans[i]);return !printf("\n");
}

你可能感兴趣的:(并查集,堆,线段树合并)