CF 381(2) D. dfs序,二分,数组模拟维护

CF 381(2)  D. Alyona and a tree   好题

题意:一棵树,每个点有权值ai,每条边有边权wi。对于两点u,v,当且仅当v是u的子孙且dis(u,v)<=av,称u控制v。求每个点控制有多少个点。

题解:dis(u,v)<=av转化为dis(r,u)>=dis(r,v)-av。然后按dfs序搜索,模拟栈操作,在栈中的点肯定是在同一条链上。当搜索到ai这个点,二分查找一下栈中哪一段点满足,给这一段点加1。这里用线段树维护很麻烦,可以像一维数组一样维护,比如数组c[],区间(l,r)要加1,只要再用一个数组f[]标记,f[l]+=1,f[r+1]-=1,计算时s[i]=s[i-1]+f[i]。

注:   1、首先要想到根据dfs序模拟出栈操作,并且在dfs时一直都是一条链。  2、二分用函数时有点纠结,以后用二分函数时应该慢一点,要特别注意范围、首尾点以及从0还是从1开始。  3在树上像一维数组一样维护有点难想。

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
#define FF(i,a,b) for (int i=a;i<=b;i++)
#define F(i,b,a)  for (int i=b;i>=a;i--)
#define mes(a,b)  memset(a,b,sizeof(a))
#define INF 0x3f3f3f3f
typedef long long ll;
const int N = 2e5+10;

struct Edge{ int to, w, next; }edge[N];
int n, a[N], cnt[N], ans[N], k, tot, head[N];
ll dis[N], sum;
void Addedge(int u, int v, int w)
{
    edge[tot].to=v;
    edge[tot].w=w;
    edge[tot].next=head[u];
    head[u]=tot++;
}
void dfs(int u, int e)
{
    if(u!=1) k++, dis[k]=dis[k-1]+edge[e].w;
    for(int i=head[u]; i!=-1; i=edge[i].next) {
        dfs(edge[i].to, i);
    }
    if(k-1>=0) cnt[k-1]+=cnt[k];
    ans[u]=cnt[k];
    int m=lower_bound(dis, dis+k+1, dis[k]-a[u])-dis;   //注意
    if(m-1>=0) cnt[m-1]--;
    if(k-1>=0) cnt[k-1]++;
    cnt[k]=dis[k]=0;    //退栈时注意清0,后面可能还要用到
    k--;
}
int main()
{
    scanf("%d", &n);
    FF(i,1,n) scanf("%d", &a[i]);
    mes(head, -1);
    FF(i,2,n) {
        int pi, wi;
        scanf("%d%d", &pi, &wi);
        Addedge(pi, i, wi);
    }
    k=0, dis[0]=0;
    mes(ans, 0);  mes(cnt, 0);
    dfs(1,head[1]);
    FF(i,1,n) printf("%d ", ans[i]);

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/sbfhy/p/6363880.html