[BZOJ4003]JLOI2015城池占领|可并堆

  一开始用倍增做,后来发现不但会MLE精度也会炸掉。。学了一下左偏树,然后就是对每个点建一个可并小根堆,然后dfs的时候向上合并,向上的时候要像线段树之类的一样打lazy标记,由于乘的数都是正的所以相对大小不会变,并上去之后不停弹堆顶然后做做记录就行了。。

#include<iostream>
#include<cstdio>
#define ll long long
#define N 300005
#define lc(x) heap[x].lc
#define rc(x) heap[x].rc
#define key(x) heap[x].key
#define q(x) heap[x].q
#define kt(x) heap[x].kt
#define bt(x) heap[x].bt
using namespace std;
struct node{
    ll q,kt,bt;
    int key,lc,rc;
} heap[N];
struct edge{
    int e,next;
}ed[N];
ll t,k[N],b[N],h[N],s[N];
int n,m,i,j,kind,ne=0,a[N],c[N],die[N],get[N],fa[N],root[N],d[N];
void add(int s,int e)
{
    ed[++ne].e=e;ed[ne].next=a[s];a[s]=ne;
}
void mark(int x,ll k,ll b)
{
    if (!x) return;
    kt(x)*=k;bt(x)*=k;bt(x)+=b;
    q(x)=q(x)*k+b;
}
void down(int x)
{
    mark(lc(x),kt(x),bt(x));mark(rc(x),kt(x),bt(x));
    kt(x)=1;bt(x)=0;
}
int merge(int x1,int x2)
{
    if (x1*x2==0) return x1+x2;
    down(x1);down(x2);
    if (q(x1)>q(x2)) swap(x1,x2);
    rc(x1)=merge(rc(x1),x2);
    if (key(rc(x1))>key(lc(x1))) swap(lc(x1),rc(x1));
    key(x1)=key(rc(x1))+1;
    return x1;
}
void dfs(int x)
{
    die[x]=0;
    for (int j=a[x];j;j=ed[j].next)
    {
        int to=ed[j].e;
        d[to]=d[x]+1;
        dfs(to);
        mark(root[to],k[to],b[to]);
        root[x]=merge(root[x],root[to]);
    }
    while (root[x]&&q(root[x])<h[x]) down(root[x]),get[root[x]]=d[c[root[x]]]-d[x],root[x]=merge(lc(root[x]),rc(root[x])),die[x]++;
}
int main()
{
    freopen("4003.in","r",stdin);
    freopen("my.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++) scanf("%I64d",&h[i]),a[i]=root[i]=die[i]=0;
    for (i=2;i<=n;i++)
    {
        scanf("%d%d%I64d",&fa[i],&kind,&t);
        if (!kind) k[i]=1,b[i]=t; else k[i]=t,b[i]=0;
        add(fa[i],i);
    }
    for (i=1;i<=m;i++)
    {
        scanf("%I64d%d",&s[i],&c[i]);
        get[i]=lc(i)=rc(i)=bt(i)=key(i)=0;
        kt(i)=1;q(i)=s[i];
        root[c[i]]=merge(root[c[i]],i);
    }
    d[1]=0;
    dfs(1);
    while(root[1]) get[root[1]]=d[c[root[1]]]-d[1]+1,root[1]=merge(lc(root[1]),rc(root[1]));
    for (i=1;i<=n;i++) printf("%d\n",die[i]);
    for (i=1;i<=m;i++) printf("%d\n",get[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值