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; }