题面
https://www.luogu.com.cn/problem/P3261
分析
考虑对于每个点开堆维护子树中到达这里的所有人的攻击力
对于攻击力改变可以用标记数组给堆打上标记
自下向上合并时,选择较小的堆合并给较大的堆,注意修改每个节点对应的堆编号
当一个值要合并入一个有标记的堆时,直接给这个值除标记值或者减标记值消除标记的影响
记录一下哪些值在哪里被从堆中删除即可
代码
//Tempestissimo #include <iostream> #include <cstdio> #include <queue> using namespace std; typedef long long ll; const int N=3e5+10; struct Graph { int v,nx; }g[N]; int list[N],cnt,a[N],c[N],dep[N],dth[N],bel[N],ans[N]; ll h[N],v[N],pls[N],multi[N]; int n,m; ll GCD(ll a,ll b) {return !b?a:GCD(b,a%b);} struct Num { int id; ll num,deno; friend bool operator < (Num a,Num b) {return (long double)a.num/a.deno>(long double)b.num/b.deno;} friend Num operator * (Num a,ll b) {ll gcd=GCD(a.deno,b);a.deno/=gcd;b/=gcd;a.num*=b;return a;} friend Num operator / (Num a,ll b) {ll gcd=GCD(a.num,b);a.num/=gcd;b/=gcd;a.deno*=b;return a;} friend Num operator + (Num a,ll b) {a.num+=b*a.deno;ll gcd=GCD(a.num,a.deno);a.num/=gcd;a.deno/=gcd;return a;} }; priority_queue<Num> q[N]; void Add(int u,int v) {g[++cnt]=(Graph){v,list[u]};list[u]=cnt;} void Merge(int &x,int &y) { if (q[x].size()<q[y].size()) swap(x,y); while (!q[y].empty()) { Num temp=q[y].top();q[y].pop(); temp=(((temp*multi[y])+pls[y])+(-pls[x]))/multi[x]; q[x].push(temp); } } void DFS(int u) { for (int i=list[u];i;i=g[i].nx) dep[g[i].v]=dep[u]+1,DFS(g[i].v),Merge(bel[u],bel[g[i].v]); while (!q[bel[u]].empty()) { Num temp=q[bel[u]].top(); temp=(temp*multi[bel[u]])+pls[bel[u]]; if ((long double)temp.num/temp.deno>=(long double)h[u]) break; ans[dth[temp.id]=u]++;q[bel[u]].pop(); } if (a[u]) multi[bel[u]]*=v[u],pls[bel[u]]*=v[u]; else pls[bel[u]]+=v[u]; } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) scanf("%lld",&h[i]),bel[i]=i,multi[i]=1; for (int i=2,f;i<=n;i++) scanf("%d%d%lld",&f,&a[i],&v[i]),Add(f,i); for (int i=1;i<=m;i++) {ll s;scanf("%lld%d",&s,&c[i]);q[c[i]].push((Num){i,s,1});} dep[1]=1;DFS(1); for (int i=1;i<=n;i++) printf("%d ",ans[i]); for (int i=1;i<=m;i++) printf("%d ",dep[c[i]]-dep[dth[i]]); }