洛谷 P3258 [JLOI2014]松鼠的新家(LCA+树上差分)

Description:

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在”树“上。

松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,…,最后到an,去参观新家。可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

维尼是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

Input:

第一行一个整数n,表示房间个数第二行n个整数,依次描述a1-an

接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。

2<= n <=300000

Output:

一共n行,第i行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。

Sample Input:

5
1 4 5 3 2
1 2
2 4
2 3
4 5

Sample Output:

1
2
1
2
1

题目链接

按照顺序走树上顶点,经过的顶点点权++,求每个顶点的最小点权。

用树上差分对相邻两点路径上经过的所有点进行计数,因为总路径起始两点外所有点会被计算两次(起点+终点),所以最后减去一次,而总路径终点不放糖,所以也需要减去一次。

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 3e5 + 5;

struct Edge {
    int V, Next;
};

Edge edges[maxn << 1];
int Head[maxn];
int Tot;

void Init() {
    Tot = 0;
    memset(Head, -1, sizeof(Head));
}

void AddEdge(int U, int V) {
    edges[Tot] = Edge {V, Head[U]};
    Head[U] = Tot++;
}

int Rmq[maxn << 1];

struct ST {
    int Dp[maxn << 1][20];

    void Init(int N) {
        for (int i = 1; i <= N; ++i) {
            Dp[i][0] = i;
        }
        for (int j = 1; (1 << j) <= N; ++j) {
            for (int i = 1; i + (1 << j) - 1 <= N; ++i) {
                Dp[i][j] = Rmq[Dp[i][j - 1]] < Rmq[Dp[i + (1 << (j - 1))][j - 1]] ? Dp[i][j - 1] : Dp[i + (1 << (j - 1))][j - 1];
            }
        }
    }

    int Query(int Left, int Right) {
        if (Left > Right) {
            swap(Left, Right);
        }
        int Len = int(log2(Right - Left + 1));
        return Rmq[Dp[Left][Len]] <= Rmq[Dp[Right - (1 << Len) + 1][Len]] ? Dp[Left][Len] : Dp[Right - (1 << Len) + 1][Len];
    }
};

int Vertex[maxn << 1];
int First[maxn];
int Parent[maxn];
int LCATot;
ST St;

void LCADfs(int Cur, int Pre, int Depth) {
    Vertex[++LCATot] = Cur;
    First[Cur] = LCATot;
    Rmq[LCATot] = Depth;
    Parent[Cur] = Pre;
    for (int i = Head[Cur]; ~i; i = edges[i].Next) {
        if (edges[i].V == Pre) {
            continue;
        }
        LCADfs(edges[i].V, Cur, Depth + 1);
        Vertex[++LCATot] = Cur;
        Rmq[LCATot] = Depth;
    }
}

void LCAInit(int Root, int NodeNum) {
    LCATot = 0;
    LCADfs(Root, 0, 0);
    St.Init(2 * NodeNum - 1);
}

int QueryLCA(int U, int V) {
    return Vertex[St.Query(First[U], First[V])];
}

int N;
int A[maxn];
int Cnt[maxn];

int Dfs(int Cur, int Pre) {
    for (int i = Head[Cur]; ~i; i = edges[i].Next) {
        if (edges[i].V == Pre) {
            continue;
        }
        Cnt[Cur] += Dfs(edges[i].V, Cur);
    }
    return Cnt[Cur];
}

int main(int argc, char *argv[]) {
    Init();
    scanf("%d", &N);
    for (int i = 1; i <= N; ++i) {
        scanf("%d", &A[i]);
    }
    for (int i = 1, X, Y; i < N; ++i) {
        scanf("%d%d", &X, &Y);
        AddEdge(X, Y);
        AddEdge(Y, X);
    }
    Parent[1] = 0;
    LCAInit(1, N);
    memset(Cnt, 0, sizeof(Cnt));
    for (int i = 1; i < N; ++i) {
        int LCA = QueryLCA(A[i], A[i + 1]);
        Cnt[A[i]]++; Cnt[A[i + 1]]++;
        Cnt[LCA]--; Cnt[Parent[LCA]]--;
    }
    Dfs(1, 0);
    for (int i = 2; i <= N; ++i) {
        Cnt[A[i]]--;
    }
    for (int i = 1; i <= N; ++i) {
        printf("%d\n", Cnt[i]);
    }
    return 0;
}
全部评论

相关推荐

最近和朋友聊天,她说了句让我震惊的话:"我发现我连周末点外卖都开始'最优解'了,一定要赶在高峰期前下单,不然就觉得自己亏了。"这不就是典型的"班味入侵"吗?工作思维已经渗透到生活的方方面面。
小型域名服务器:啊?我一直都这样啊?我还以为是我爱贪小便宜呢?每次去实验室都得接一杯免费的开水回去,出门都得规划一下最短路径,在宿舍就吃南边的食堂,在实验室就吃北边的食堂,快递只有顺路的时候才取。
点赞 评论 收藏
分享
10-25 00:32
香梨想要offer:感觉考研以后好好学 后面能乱杀,目前这简历有点难
点赞 评论 收藏
分享
头像
11-18 16:08
福州大学 Java
影流之主:干10年不被裁,我就能拿别人一年的钱了,日子有盼头了
点赞 评论 收藏
分享
预计下个星期就能开奖吧,哪位老哥来给个准信
华孝子爱信等:对接人上周说的是这周
投递华为等公司10个岗位 >
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务