bzoj 4003 [JLOI2015]城池攻占 —— 左偏树

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4003

其实蛮简单的,首先一个城市只会被其子树中的骑士经过,启发我们 dfs 序用可并堆合并子树信息;

先乘后加,和带乘法的线段树一个方法;

如果秒 WA 的话,把读入全写成 %lld 就好了...

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
int const maxn=3e5+5;
int n,m,c[maxn],rt[maxn],a[maxn],dis[maxn],dep[maxn];
ll h[maxn],v[maxn],s[maxn],la[maxn],lc[maxn];
int hd[maxn],ct,to[maxn],nxt[maxn],ans[maxn],end[maxn],ls[maxn],rs[maxn];
void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;}
void update(int x,ll c,ll a)
{
    if(!x)return;//
    s[x]*=c; s[x]+=a;
    lc[x]*=c; la[x]*=c; la[x]+=a;
}
void pushdown(int x)
{
    if(lc[x]==1&&la[x]==0)return;//
    update(ls[x],lc[x],la[x]); update(rs[x],lc[x],la[x]);
    lc[x]=1; la[x]=0;
}
int merge(int x,int y)
{
    if(!x||!y)return x+y;
    pushdown(x); pushdown(y);//
    if(s[x]>s[y])swap(x,y);
    rs[x]=merge(rs[x],y);
    if(dis[ls[x]]<dis[rs[x]])swap(ls[x],rs[x]);
    if(rs[x])dis[x]=dis[rs[x]]+1;
    else dis[x]=0;
    return x;
}
void dfs(int x,int f)
{
    dep[x]=dep[f]+1; 
    pushdown(rt[x]);
    for(int i=hd[x],u;i;i=nxt[i])
    {
        dfs(u=to[i],x); pushdown(rt[u]);
        rt[x]=merge(rt[x],rt[u]);
    }
    while(rt[x]&&s[rt[x]]<h[x])
    {
        pushdown(rt[x]);
        ans[x]++; end[rt[x]]=x;
        rt[x]=merge(ls[rt[x]],rs[rt[x]]);
    }
    if(a[x]==0)update(rt[x],1,v[x]);
    else update(rt[x],v[x],0);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&h[i]);
    for(int i=2,fa;i<=n;i++)scanf("%lld%lld%lld",&fa,&a[i],&v[i]),add(fa,i);
    for(int i=1;i<=m;i++)lc[i]=1;
    for(int i=1;i<=m;i++)
    {
        scanf("%lld%lld",&s[i],&c[i]);
        rt[c[i]]=merge(rt[c[i]],i);
//        lc[i]=1;
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
    for(int i=1;i<=m;i++)printf("%d\n",dep[c[i]]-dep[end[i]]);
    return 0;
}

 

posted @ 2018-09-06 16:25  Zinn  阅读(126)  评论(0编辑  收藏  举报