CodeForces 208E Blood Cousins(树剖求lca + dfs序)

本文介绍了一种使用树剖和DFS序来解决特定树形结构问题的方法,该问题要求查询指定结点向上k步的父节点的子树中与该结点深度相同的兄弟结点数量。

题意:
给出若干棵树,q次询问,每次询问某结点n向上k步的父节点的子树中有多少个和n是兄弟结点(深度相同)
ps:这里的题意是将问题简化过后的
假设现在只有一棵树,首先考虑结点的子树如何确定,dfs序可以解决,再考虑向上k步的lca如何解决,倍增法求lca(弱不会),由于只会树剖向上爬,外加上树剖可以直接处理dfs序,所以就写树剖了…对于子树内符合条件的结点,只需要开105个深度的vector存一下dfs序,然后每次lower_bound和upper_bound就能找到对应答案…做法没什么不对,就是从来没这样搞过,好在1A,codeforces题目还是神奇…
仔细想想这不就是在XJB搞嘛…..

#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 200003

int a[maxn];

int fir[maxn],nex[maxn],e_max,v[maxn];
int in[maxn],out[maxn];
int son[maxn],fa[maxn],deep[maxn],top[maxn],siz[maxn],cnt,tot;
int inv[maxn];

vector<int>vec[100005];

void init_()
{
    memset(fir,-1,sizeof fir);
    memset(son,-1,sizeof son);
    e_max=0;
    cnt=0;
    tot=0;
}

void add_edge(int s,int t)
{
    int e=e_max++;
    v[e]=t;
    nex[e]=fir[s];
    fir[s]=e;
}

void dfs1(int k,int pre,int d)
{
    deep[k]=d;
    fa[k]=pre;
    siz[k]=1;

    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(e!=pre)
        {
            dfs1(e,k,d+1);
            siz[k]+=siz[e];
            if(son[k]==-1||siz[son[k]]<siz[e]) son[k]=e;
        }
    }
}

void dfs2(int k,int sp)
{
    top[k]=sp;
    in[k]=++tot;
    inv[tot]=k;
    vec[deep[k]].push_back(in[k]);
    out[k]=in[k];
    if(son[k]==-1) return;
    dfs2(son[k],sp);
    out[k]=max(out[k],out[son[k]]);
    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(e!=fa[k]&&e!=son[k])
        {
            dfs2(e,e);
            out[k]=max(out[k],out[e]);
        }
    }
}

int query(int s,int k)
{
    s=fa[s];
    int f1=top[s];
    while(k)
    {
        int d=in[s]-in[f1]+1;
        if(k>d)
        {
            k-=d;
            s=fa[f1];
            f1=top[s];
        }
        else
        {
            s=inv[ in[s]-k+1 ];
            break;
        }
    }
    return s;
}

int main()
{
    init_();
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]) add_edge(a[i],i),add_edge(i,a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        if(!a[i])
        {
            dfs1(i,-1,0);
            dfs2(i,i);
        }
    }
    for(int i=1;i<=n;i++)
    {
        sort(vec[i].begin(),vec[i].end());
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(deep[a]<b||b==0)
        {
            printf("0 ");
            continue;
        }
        int lca=query(a,b);

        int d=deep[a];
        int l=lower_bound(vec[d].begin(),vec[d].end(),in[lca])-vec[d].begin();
        int r=upper_bound(vec[d].begin(),vec[d].end(),out[lca])-vec[d].begin();
        printf("%d ",r-l-1);
    }
}
/*
9
0 1  2 3 4 4 3 2 1
*/
当前提供的引用内容并未提及关于Codeforces比赛M1的具体时间安排[^1]。然而,通常情况下,Codeforces的比赛时间会在其官方网站上提前公布,并提供基于不同时区的转换工具以便参赛者了解具体开赛时刻。 对于Codeforces上的赛事而言,如果一场名为M1的比赛被计划举行,则它的原始时间一般按照UTC(协调世界时)设定。为了得知该场比赛在UTC+8时区的确切开始时间,可以遵循以下逻辑: - 前往Codeforces官网并定位至对应比赛页面。 - 查看比赛所标注的标准UTC起始时间。 - 将此标准时间加上8小时来获取对应的北京时间(即UTC+8)。 由于目前缺乏具体的官方公告链接或者确切日期作为依据,无法直接给出Codeforces M1比赛于UTC+8下的实际发生时段。建议定期访问Codeforces平台查看最新动态更新以及确认最终版程表信息。 ```python from datetime import timedelta, datetime def convert_utc_to_bj(utc_time_str): utc_format = "%Y-%m-%dT%H:%M:%SZ" bj_offset = timedelta(hours=8) try: # 解析UTC时间为datetime对象 utc_datetime = datetime.strptime(utc_time_str, utc_format) # 转换为北京时区时间 beijing_time = utc_datetime + bj_offset return beijing_time.strftime("%Y-%m-%d %H:%M:%S") except ValueError as e: return f"错误:{e}" # 示例输入假设某场Codeforces比赛定于特定UTC时间 example_utc_start = "2024-12-05T17:35:00Z" converted_time = convert_utc_to_bj(example_utc_start) print(f"Codeforces比赛在北京时间下将是:{converted_time}") ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值