JLOI2014 松鼠的新家 LCA+树上差分

传送门

题解:很简单,就是求树上点的差分。把从a[i]到a[i+1]的路径上的点覆盖一次,记录总覆盖次数
点的差分和边的差分略有不同,不过大体思路都是差不多的,边的差分是在起点,终点tag+1,LCA处-2;而点的差分是起点,终点tag+1,LCA及其父亲tag-1。为什么?模拟一下就行了
然后求个子树和.jpg
注意这道题起点和终点是连在一起的,所以除了第一条之外的所有起点都要tag-1。

#include
#include
#include
using namespace std;
const int MAXN = 300001;

int a[MAXN], tag[MAXN], Ans[MAXN];
int fir[MAXN], nxt[MAXN << 1], to[MAXN << 1], cnt;
int fa[MAXN][20], dep[MAXN];

inline int read(){
    int k = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
    return k * f;
}

inline void add_edge(int a, int b){
    to[cnt] = b;
    nxt[cnt] = fir[a];
    fir[a] = cnt++;
}

void dfs1(int u, int f){
    dep[u] = dep[f] + 1;
    fa[u][0] = f;
    for(int i = 1; (1 << i) <= dep[u]; i++){
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
    }
    for(int i = fir[u]; i != -1; i = nxt[i]){
        int v = to[i];
        if(v != f) dfs1(v, u);
    }
}

int lca(int x, int y){
    if(dep[x] < dep[y]) swap(x, y);
    for(int i = 19; i >= 0; i--){
        if(dep[y] + (1 << i) <= dep[x]){
            x = fa[x][i];
        }
    }
    if(x == y) return x;
    for(int i = 19; i >= 0; i--){
        if(fa[x][i] != fa[y][i]){
            x = fa[x][i], y = fa[y][i];
        }
    }
    return fa[x][0];
}

void dfs2(int u, int f){
    Ans[u] = tag[u];
    for(int i = fir[u]; i != -1; i = nxt[i]){
        int v = to[i];
        if(v == f) continue;
        dfs2(v, u);
        Ans[u] += Ans[v];
    }
}

int main(){
    memset(fir, -1, sizeof(fir));
    int n = read();
    for(int i = 1; i <= n; i++){
        a[i] = read();
    }
    for(int i = 1; i < n; i++){
        int x = read(), y = read();
        add_edge(x, y), add_edge(y, x);
    }
    dep[0] = -1;
    dfs1(1, 0);
    for(int i = 1; i < n; i++){
        int LCA = lca(a[i], a[i + 1]);
        tag[a[i]]++, tag[a[i + 1]]++, tag[LCA]--, tag[fa[LCA][0]]--;
    }
    dfs2(1, 0);
    for(int i = 2; i <= n; i++) Ans[a[i]]--;
    for(int i = 1; i <= n; i++){
        printf("%d\n", Ans[i]);
    }
    return 0;
}

你可能感兴趣的:(图论,LCA,树上差分)