Bug2

Bug2

题目描述

Bug2

之前的战斗使虫族的建筑遭到破坏,Bug需要组织大家一起重建被破坏的建筑。不过,为了合理分配任务,他需要仔细地考虑虫族的关系网络。

虫族的基地组织是一棵有根树,基地可看作是树的节点。每个基地i都有自身的影响力ai。树的边有一定的长度,记dis(u,v)表示基地u和基地v的距离。

关系网络的复杂性体现在两个基地可能会有控制的关系,我们说基地u控制基地v当且仅当:

  • u是v的祖先;
  • 且dis(u,v)≤av

Bug为了合理分配每个基地的任务,需要了解每个基地控制多少个基地。作为虫族总参谋,你能帮帮他吗?

输入格式

第一行一个正整数N,表示基地的个数。

第二行N个整数,第i个表示ai。1 ≤ ai ≤ 109

接下来N-1行,每行两个整数a,b。输入的第2+i行,表示基地i+1和基地a之间有一条边,且边的长度为b。1 ≤ a ≤ i;1 ≤ b ≤ 109

输出格式

一行,N个整数,第i个表示基地i控制多少个基地。

输入样例1
5
2 5 1 4 6
1 7
1 1
3 5
3 6
输出样例1
1 0 1 0 0

    这题的暴力,其实并不难想。在考试时,就想到了。但是,当时心烦意乱,想得不太清楚,又调了很久,仍然完美爆零。下次,暴力也一定要想清楚再写……

    暴力方法:对原图进行DFS。在DFS时,将当前点的所有祖先存入一个栈当中。从后面的祖先往前扫,如果dis(u,v)≤av,则祖先的控制力++。最后即可得出正确答案。

    dis(u,v)怎么求?不要笨笨地DFS,稍微转化一下,即可知道dis(u,v)=d[v]-d[u];(u,v与祖先的距离差值)。

  (PS:转化思想一定要时时挂在心头)

    正解,其实就是对暴力的小小优化。通过分析可以发现,如果u不能控制v,则u上方的其它v的祖先也一定不能控制v;如果u能控制v,则u下方的其它v的祖先也一定能控制v。这其中就有单调性。

    因此,可以用二分查找找出最上方的能够控制v的祖先up,将up及其以下的其它v的祖先的控制力都加1。但是显然,for循环累加会造成超时。怎么办呢?

    我们需要修改的元素,在栈中是连续的。修改区间值,可以用到线段树。不过较为复杂,这里不写。

    另一种方法,是在树上做差分。

    差分是什么?就是之前曾经接触过的某个DP模型。假设有n个值,每次将第s个到第t个都加上某个数y, 最后需要回答每个值的最终值。数据量大的情况下,直接for循环累加,会超时。解决方法是:f[s]+=y;f[t+1]-=y。第i个值的变化值即为f[1]+f[2]+……+f[i]的和。具体说明详见差分的题解(洛谷 语文成绩)。

    类似的,此处我们同样可以用差分。分析得知,从栈顶元素到up的控制力都要加1。可以这样操作:f[st[top]]++;F[st[up-1]]--。最后每个结点的控制力即为它及其子孙的f值之和。原理不难理解,可自行脑补。

    时间复杂度:O(NlogN)

    代码如下:

#include
   
   
    
    //printf
#include
    
    
     
     
#include
     
     
      
      
using namespace std;
const int MAXN=1e5+5;
struct poi
{
	int to;
	long long len;
};
vector
      
      
       
       edge[MAXN];
int n,end,an[MAXN],from,len,st[MAXN],z,f[MAXN];
long long dis[MAXN],c[MAXN],a[MAXN];
void dfs(int now)
{
	int lo=0,hi=z+1;
	//(lo,hi]
	while(lo+1
       
       
         =dis[now]-a[now]) hi=mid; else lo=mid; } if(hi<=z) { f[st[z]]++; f[st[lo]]--; } st[++z]=now; int size=edge[now].size(); for(int i=0;i 
        
          >n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=2;i<=n;i++) { cin>>from>>len; edge[from].push_back((poi){i,len}); } dfs(1); run(1); for(int i=1;i<=n;i++) printf("%d ",an[i]); return 0; } 
         
       
      
      
     
     
    
    
   
   



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值