bzoj3011: [Usaco2012 Dec]Running Away From the Barn 可並堆(左偏樹)

題意:給出以1號點爲根的一棵有根樹,問每個點的子樹中與它距離小於l的點有多少個。n<=2e5,l<=1e18

隨手抽了一道,以爲是個水題,刷水不成反被刷。
一開始覺得這題不難,然後樣例老過不去,然後看zyf2000的代碼。。還是錯的,最後直接拿她的交了一下,結果是錯的。。一怒之下刪掉重寫。。
開始想的是倍增什麼的,但是好像不好動態維護,而且複雜度好大。。
然後這就只能可並堆咯,堆維護每個子樹內距離不超過L的點,從下往上走,走完一個子樹就合併一波,然後動態把距離超過L的刪掉就可以了。
很久沒打左偏樹,好醜。。
需要注意的是d[i]是左偏樹的dist,dis[i]則是i到根的距離,剛好與左偏樹的優先級是相同的。
記錄lazy[x]來傳遞權值。f[x]表示x所在的堆的top。


#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=4e5+5;
typedef long long ll;
int head[N],next[N],go[N],d[N];
int ans[N],n;
ll dis[N],lazy[N],val[N],L;
int ls[N],rs[N],f[N],size[N],tot;
inline void add(int x,int y,ll z)
{
    go[++tot]=y;
    val[tot]=z;
    next[tot]=head[x];
    head[x]=tot;
}
inline void solve(int x,ll v)
{
    if (!x)return;
    dis[x]+=v;
    lazy[x]+=v;
}
inline void pushup(int x)
{
    size[x]=size[ls[x]]+size[rs[x]]+1;
}
inline void pushdown(int x)
{
    if (lazy[x])solve(ls[x],lazy[x]),solve(rs[x],lazy[x]),lazy[x]=0;
}
inline int merge(int x,int y)
{
    if (!x)return y;
    if (!y)return x;
    if (dis[x]<dis[y])swap(x,y);
    pushdown(x);
    rs[x]=merge(rs[x],y);
    if (d[ls[x]]<d[rs[x]])swap(ls[x],rs[x]);
    d[x]=d[rs[x]]+1;
    pushup(x);
    return x;
}
inline void pop(int x)
{
    pushdown(f[x]);
    f[x]=merge(ls[f[x]],rs[f[x]]);
}
inline void dfs(int x,int fa)
{
    f[x]=x,d[x]=size[x]=1;
    for(int i=head[x];i;i=next[i])
    {
        int v=go[i];
        if (v!=fa)
        {
            dfs(v,x);
            solve(f[v],val[i]);
            while (dis[f[v]]>L)pop(v);
            f[x]=merge(f[x],f[v]);
        }
    }
    ans[x]=size[f[x]];
}
int main()
{
    scanf("%d%lld",&n,&L);
    fo(i,2,n)
    {
        int x;
        ll z;
        scanf("%d%lld",&x,&z);
        add(x,i,z);
        add(i,x,z);
    }
    dfs(1,0);
    fo(i,1,n)printf("%d\n",ans[i]);
}
發佈了678 篇原創文章 · 獲贊 16 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章