【洛谷P3261】城池攻占
题目
题目链接:https://www.luogu.com.cn/problem/P3261
臭不要脸的把 LOJ 的题面截图下来
思路
一个显然的思路是从叶子往上跑,将所有可以到点 \(x\) 的骑士扔进一个小根堆里面,然后不停弹出堆顶直到对顶元素的值不小于 \(h[x]\) 或者堆空了。
由于需要堆合并,所以直接上左偏树即可。至于乘和加就在堆顶打 tag,然后合并或弹出的时候再 pushdown 即可。顺序显然是先乘后加。
对于第一问,答案就是在点 \(x\) 被弹出的元素个数;对于第二问,答案是该骑士最先攻击的点的深度减去弹出时点的深度。注意对于攻击完点 \(1\) 依然存活的骑士并不计入 \(1\) 的答案,且不用减去 \(dep[1]\)。
时间复杂度 \(O(n\log m)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=300010;
ll n,m,tot,head[N],fa[N],a[N],c[N],rt[N],dep[N],ans1[N],ans2[N],h[N],v[N],s[N];
struct edge
{
ll next,to;
}e[N];
void add(ll from,ll to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
struct LeftistTree
{
ll val[N],dis[N],lc[N],rc[N],lazy[N][2];
void pushdown(ll x)
{
if (lazy[x][1]!=1)
{
val[x]*=lazy[x][1];
lazy[lc[x]][1]*=lazy[x][1]; lazy[rc[x]][1]*=lazy[x][1];
lazy[lc[x]][0]*=lazy[x][1]; lazy[rc[x]][0]*=lazy[x][1];
}
if (lazy[x][0])
{
val[x]+=lazy[x][0];
lazy[lc[x]][0]+=lazy[x][0]; lazy[rc[x]][0]+=lazy[x][0];
}
lazy[x][1]=1; lazy[x][0]=0;
}
ll merge(ll x,ll y)
{
if (!x || !y) return x+y;
pushdown(x); pushdown(y);
if (val[x]>val[y]) swap(x,y);
rc[x]=merge(rc[x],y);
if (dis[rc[x]]>dis[lc[x]]) swap(lc[x],rc[x]);
dis[x]=dis[rc[x]]+1;
return x;
}
}lt;
void dfs(ll x)
{
dep[x]=dep[fa[x]]+1;
for (ll i=head[x];~i;i=e[i].next)
{
ll v=e[i].to;
dfs(v);
rt[x]=lt.merge(rt[x],rt[v]);
}
lt.pushdown(rt[x]);
while (rt[x] && lt.val[rt[x]]<h[x])
{
ans1[x]++;
ans2[rt[x]]=dep[c[rt[x]]]-dep[x];
rt[x]=lt.merge(lt.lc[rt[x]],lt.rc[rt[x]]);
lt.pushdown(rt[x]);
}
if (a[x]==0) lt.lazy[rt[x]][0]+=v[x];
else lt.lazy[rt[x]][1]*=v[x],lt.lazy[rt[x]][0]*=v[x];
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=n;i++)
scanf("%lld",&h[i]);
for (ll i=2;i<=n;i++)
{
scanf("%lld%lld",&fa[i],&a[i]);
scanf("%lld",&v[i]);
add(fa[i],i);
}
lt.dis[0]=-1;
for (ll i=1;i<=m;i++)
{
scanf("%lld",&s[i]); scanf("%lld",&c[i]);
lt.val[i]=s[i]; lt.lazy[i][1]=1;
rt[c[i]]=lt.merge(rt[c[i]],i);
}
dfs(1);
lt.pushdown(rt[1]);
while (rt[1])
{
ans2[rt[1]]=dep[c[rt[1]]];
rt[1]=lt.merge(lt.lc[rt[1]],lt.rc[rt[1]]);
lt.pushdown(rt[1]);
}
for (ll i=1;i<=n;i++) printf("%lld\n",ans1[i]);
for (ll i=1;i<=m;i++) printf("%lld\n",ans2[i]);
return 0;
}