首先,这道题有非常简单的暴力写法,只要对每个人暴力一步步往上走即可。如何优化暴力呢?最容易想到的方法是加速转移过程,即倍增,但空间貌似不太够...那么我们从另一个角度考虑:利用重复的信息。同一段路可能被很多人走过,造成了极大的时间浪费,我们可以考虑把要走同一段路的人集中在一起,这样只要走一次,就能同时判断每个人能否走下去了。怎么操作呢?我们以每一个城池为根建立小根堆,堆中的元素是一个个骑士的战斗力,每次判断时,把所有战斗力不足的骑士弹出,剩下的骑士利用可并堆的合并操作合并到下一个城池。那么问题来了,每次攻占城池后,所有存活骑士的战斗力都会发生变化,逐个修改肯定会超时。我们会发现,修改每个骑士战斗力的操作十分类似线段树的区间修改,因此,我们可以利用优化时间复杂度。完整代码如下。
#include<cstdio>
#define maxn 300010
#define r register
#define cswap(_x,_y) (_x^=_y,_y^=_x,_x^=_y)
#define add(_x,_y) (s[_x]+=_y,tagb[_x]+=_y)//打加法标记
#define mul(_x,_y) (s[_x]*=_y,tagb[_x]*=_y,tagk[_x]*=_y)//打乘法标记
using namespace std;
int n,m,a,fa[maxn],c[maxn],ansn[maxn],ansm[maxn];
int dep[maxn],root[maxn],dis[maxn],ls[maxn],rs[maxn];
long long h[maxn],k[maxn],b[maxn],s[maxn],tagk[maxn],tagb[maxn];
template<class t>void read(r t &x)
{
r char ch;
while(ch=getchar(),(ch<'0'||ch>'9')&&ch!='-');
r bool neg=ch=='-';x=neg?0:ch-'0';
while(ch=getchar(),ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0';
if(neg) x=-x;
}
void pushdown(r int x)//切记先乘再加
{
if(tagk[x]!=1)
{
if(ls[x]) mul(ls[x],tagk[x]);
if(rs[x]) mul(rs[x],tagk[x]);
tagk[x]=1;
}
if(tagb[x])
{
if(ls[x]) add(ls[x],tagb[x]);
if(rs[x]) add(rs[x],tagb[x]);
tagb[x]=0;
}
}
int merge(r int x,r int y)
{
if(!x||!y) return x+y;
if(s[x]>s[y]) cswap(x,y);
pushdown(x),rs[x]=merge(rs[x],y);
if(dis[ls[x]]<dis[rs[x]]) cswap(ls[x],rs[x]);
dis[x]=dis[rs[x]]+1;
return x;
}
void dfs(r int x)
{
ansm[x]=dep[c[x]];
if(ls[x]) dfs(ls[x]);
if(rs[x]) dfs(rs[x]);
}
int main()
{
read(n),read(m);
for(r int i=1;i<=n;i++) read(h[i]);
for(r int i=2;i<=n;i++)//把加法和乘法操作转化为kx+b的形式,避免分类讨论
{
read(fa[i]),read(a);
if(a) read(k[i]),b[i]=0;
else read(b[i]),k[i]=1;
}
for(r int i=1;i<=n;i++) dep[i]=dep[fa[i]]+1;
for(r int i=1;i<=m;i++)
{
read(s[i]),read(c[i]),tagk[i]=1;
root[c[i]]=merge(root[c[i]],i);
}
for(r int i=n;i;i--)//自底向上更新答案
{
while(root[i]&&s[root[i]]<h[i])//弹出战斗力不足的骑士
{
ansn[i]++;
ansm[root[i]]=dep[c[root[i]]]-dep[i];
pushdown(root[i]);
root[i]=merge(ls[root[i]],rs[root[i]]);
}
if(root[i])//如果还有存活的骑士,打上标记,合并到下个堆中
{
if(b[i]) add(root[i],b[i]);
if(k[i]!=1) mul(root[i],k[i]);
root[fa[i]]=merge(root[fa[i]],root[i]);
}
}
dfs(root[0]);//攻下所有城池的骑士保存在以0为根的堆中,会被遗漏,需要特判
for(r int i=1;i<=n;i++) printf("%d\n",ansn[i]);
for(r int i=1;i<=m;i++) printf("%d\n",ansm[i]);
return 0;
}