可并堆
Posted Rivendell
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可并堆相关的知识,希望对你有一定的参考价值。
左偏树
bzoj4003 城池攻占
题目大意:一棵树,每个点有一个防御值。m个武士,有攻击力和起始的位置,攻下一个点后会向父亲进攻,攻击力大于等于一个点的防御力就可以攻下,否则死亡。武士攻下每个点后攻击力会变化,加上或者乘上一个数(乘的数保证非负)。问每个城市死亡的武士个数和每个武士攻下的点。
思路:用左偏树维护一个点的武士信息,权值的更改用*a+b的形式更新。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define N 300005 #define LL long long using namespace std; struct use{int l,r,d;LL ad,ch;}hp[N]; struct uu{int ai;LL vi,hi;}ci[N]; int rt[N]={0},point[N]={0},next[N],cur[N],zh[N],zt,pa[N],ca[N]={0},dis[N]={0}; LL vi[N]; void pushdown(int x){ int l,r;l=hp[x].l;r=hp[x].r; if (l){ vi[l]=vi[l]*hp[x].ch+hp[x].ad; hp[l].ad=hp[l].ad*hp[x].ch+hp[x].ad; hp[l].ch=hp[l].ch*hp[x].ch; }if (r){ vi[r]=vi[r]*hp[x].ch+hp[x].ad; hp[r].ad=hp[r].ad*hp[x].ch+hp[x].ad; hp[r].ch=hp[r].ch*hp[x].ch; }hp[x].ad=0LL;hp[x].ch=1LL; } int merge(int x,int y){ if (!x) return y; if (!y) return x; pushdown(x);pushdown(y); if (vi[x]>vi[y]) swap(x,y); hp[x].r=merge(hp[x].r,y); if (hp[hp[x].l].d<hp[hp[x].r].d) swap(hp[x].l,hp[x].r); if (!hp[x].r) hp[x].d=0; else hp[x].d=hp[hp[x].r].d+1; return x;} void work(int u){ while(rt[u]&&vi[rt[u]]<ci[u].hi){ ++ca[u];pushdown(rt[u]); pa[rt[u]]=dis[pa[rt[u]]]-dis[u]; rt[u]=merge(hp[rt[u]].l,hp[rt[u]].r); }if (u!=1){ if (rt[u]){ if (ci[u].ai==0){ vi[rt[u]]+=ci[u].vi; hp[rt[u]].ad+=ci[u].vi; }else{ vi[rt[u]]*=ci[u].vi; hp[rt[u]].ad*=ci[u].vi; hp[rt[u]].ch*=ci[u].vi; } } }else while(rt[u]){ pushdown(rt[u]); pa[rt[u]]=dis[pa[rt[u]]]-dis[u]+1; rt[u]=merge(hp[rt[u]].l,hp[rt[u]].r); } } int main(){ int n,m,i,fi,u,v; scanf("%d%d",&n,&m); for (i=1;i<=n;++i) scanf("%I64d",&ci[i].hi); for (i=2;i<=n;++i){ scanf("%d%d%I64d",&fi,&ci[i].ai,&ci[i].vi); next[i]=point[fi];cur[fi]=point[fi]=i; }for (i=1;i<=m;++i){ scanf("%I64d%d",&vi[i],&fi); pa[i]=fi; hp[i]=(use){0,0,0,0LL,1LL}; rt[fi]=merge(rt[fi],i); }zh[zt=1]=1;dis[1]=1; while(zt){ u=zh[zt]; if ((v=cur[u])>0){ dis[v]=dis[u]+1; zh[++zt]=v; cur[u]=next[v]; }else{ --zt; for (v=point[u];v;v=next[v]) rt[u]=merge(rt[u],rt[v]); work(u); } }for (i=1;i<=n;++i) printf("%d\n",ca[i]); for (i=1;i<=m;++i) printf("%d\n",pa[i]); }
以上是关于可并堆的主要内容,如果未能解决你的问题,请参考以下文章