【算法训练】CodeForces208E Blood Cousins(树上启发合并)

题意

在一棵树上,如果两个节点a和b向上的第p个祖先相同,就称他们为p代表亲。(跟日常生活中有所不同,p不一定是他们的最近公共祖先)。
给出一些询问,问v的p代表亲的数量。

题解

对于每个询问,我们可以理解为,在结点v的p级祖先的子树中,和v深度相同的节点有多少个。
对于递归来说,是从叶子节点开始寻找答案的,也就是自底向上求答案、如果不知道v的p级祖先是谁,我们就无法离线处理询问。
所以对于每一个询问,我们先要找出每个节点v的p级祖先。这个用一遍dfs的方法就可以做到,方法如下。
首先保存好每一个询问,然后对树做一遍dfs,同时记录每个节点的深度和每个对应深度的下标,如果有重复的就覆盖掉。在dfs到某一节点v时,处理v的所有询问,他的p即祖先就为 i n d e x [ n o w d e p a s k . d e p ] 。推导用深度的关系,纸笔手祘一下就OK。注意 n o w d e p a s k . d e p <= 0 的时候,因为节点从1开始编号,小于0就没有意义,相当于答案为0。
剩下的就是dsu on tree,处理一下各深度的节点个数,然后依次处理询问就OK了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
const int nmax = 1e5+7;
const int INF = 0x3f3f3f3f;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
const ull p = 67;
const ull MOD = 1610612741;
vector<int> root;
int tot,tot_ask,tot_final,deep[nmax],son[nmax],sz[nmax],index[nmax],cnt[nmax],ans[nmax];
bool visit[nmax];
int head[nmax],head_ask[nmax],head_final[nmax];
struct qnode{int to,nxt,id;}final_ask[nmax<<1];
struct node{int to,nxt,id;}ask[nmax<<1];
struct edge{int to,nxt;}e[nmax<<1];
void add_edge(int u, int v){e[tot].to = v, e[tot].nxt = head[u], head[u] = tot++;}
void add_ask(int u, int v,int id){
    ask[tot_ask].id = id;
    ask[tot_ask].to = v;
    ask[tot_ask].nxt = head_ask[u];
    head_ask[u] = tot_ask++;
}
void add_final(int u, int v, int id){
    final_ask[tot_final].to = v;
    final_ask[tot_final].nxt = head_final[u];
    final_ask[tot_final].id = id;
    head_final[u] = tot_final++;
}
void dfs1(int u, int f, int d){
    deep[u] = d, sz[u] = 1, index[deep[u]] = u;
    for(int i = head_ask[u];i!=-1;i=ask[i].nxt){
        int dep = ask[i].to;
        if(deep[u] - dep <= 0) ans[ask[i].id] = 0;
        else add_final(index[deep[u] - dep],dep,ask[i].id);
    }
    for(int i = head[u];i!=-1;i=e[i].nxt){
        int v = e[i].to;
        if(v != f){
            dfs1(v,u,d+1); sz[u] += sz[v];
            if(sz[son[u]] < sz[v]) son[u] = v;
        }
    }
}
void update(int u, int f, int tag){
    cnt[deep[u]] += tag;
    for(int i = head[u];i!=-1;i=e[i].nxt){
        int v = e[i].to;
        if(v != f && !visit[v]) update(v,u,tag);
    }
}
void dfs2(int u, int f, bool keep = false) {
    for(int i = head[u]; i!= -1;i=e[i].nxt) {
        int v = e[i].to;
        if(v != f && v != son[u]) dfs2(v,u);
    }
    if(son[u]) dfs2(son[u],u,true), visit[son[u]] = true;
    update(u,f,1);
    for(int i = head_final[u];i!=-1;i=final_ask[i].nxt){
        int dep = final_ask[i].to;
        ans[final_ask[i].id] = cnt[deep[u] + dep] - 1 ;
    }
    if(son[u]) visit[son[u]] = false;
    if(!keep) update(u,f,-1);
}
int n,m;
int main(){
    memset(head,-1,sizeof head);
    memset(head_ask,-1,sizeof head_ask);
    memset(head_final,-1,sizeof head_final);
    scanf("%d",&n);
    int ff;
    for(int i = 1;i<=n;++i){
        scanf("%d",&ff);
        if(ff == 0) {
            root.push_back(i);
            continue;
        }
        add_edge(i,ff),add_edge(ff,i);
    }
    scanf("%d",&m);
    int v, dep;
    for(int i = 1;i<=m;++i){
        scanf("%d %d",&v,&dep);
        add_ask(v,dep,i);
    }
    for(int i = 0;i<root.size();++i) {
        memset(index,0,sizeof index);
        dfs1(root[i],-1,1);
    }
    for(int i = 0;i<root.size();++i) {
        memset(visit,0,sizeof visit);
        memset(cnt,0,sizeof cnt);
        dfs2(root[i],-1);
    }
    for(int i = 1;i<=m;++i) printf("%d ",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/pengwill97/article/details/81516132