Message 【题解】【考试】

本文介绍了一道关于信息在树形结构中传递的问题,通过树链剖分的方法优化查询效率,确保信息准确高效地从源节点传播到目标节点,并详细展示了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

为了降为Noip难度,好心出题人悄悄把数据改成了随机,这样树高就是log

题面

给定一棵树与他的根,根标号是0,有n+1个点。
现有一些信息在树上传递,每一个信息在树上两个节点传递的时间都是1s。
现每一个节点除了根有两种状态。

  • 普通状态:可以上传信息,下传信息。
  • 懵逼状态只能下传信息。

一开始所有点都是普通状态,当一个点上传信息后变为懵逼状态,当一个点接受了下传的信息后变为普通状态。某一时刻懵逼状态的点有下传到自己的信息又有上传到自己的信息,先计算下传的信息,使自己解除懵逼,再上传信息。某一时刻如果一个非懵逼的点接受到多条信息,上传那一个来源点(注:来源点是一开始上传这条信息的点,即题目中给出的这条信息的出发点)标号最小的,然后进入懵逼状态。一个信息上传到懵逼的点,懵逼的点先接受到信息,然后下传。
上传是儿子给父亲信息,下传是父亲向信息来源点方向的儿子下传信息。
一条信息重新回到来源点称作该信息完成传递。现在给出m条信息的来源点,出发时间,要求出这m个信息的分别的完成时间。

sol

题面有点扯不清楚,我已经尽量详细的翻译了
这道题一看就知道要确定某条信息从那个点开始下传(称这个点为转折点)。一般是根(0号点),但是很有可能中途就返回了。一般来说,就是某一条信息自己完成后会对他经过的路径造成影响。我们可以搞一个这样的思路:找到当前的点的转折点,然后算出答案,然后对他的路径进行某些值的修改。
这样就要求后面的点不能影响到前面的的点。
如何做到?
考虑两个点,如果两个点,谁的deep[x]+t[a](t[a]是出发时间)小,谁就会在到达某一相同高度时当前时间较小,所以按这个排序就可以了。
然后就是对于任何一个点x
如果

deep[a]+t[a]deep[x]<T[b]deep[b]+2deep[x]deep[a]+t[a]−deep[x]<T[b]−deep[b]+2∗deep[x]

b是之前影响到x的信息的来源点,T是结束时间,t是开始时间,那么a到了x就要返回。那么肯定是二分找最浅的x。这样在树上,就可以使用树链剖分.
但是如果在最外边二分深度,里面log2log2查询,就会TLE所以我们换一个搞法,就是往上面跳重链,对于一条重链直接查询,如果该重链内有大于deep[a]+t[a]的,再在这一截线段树上用二分,否则跳上一个重链。

code

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

inline char gc(){
    static char buf[1<<6],*p1=buf,*p2=buf;
    return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,1<<6,stdin),p1==p2)?EOF:*p1++;
}
template <class T>
inline void read(T&data){
    data=0;
    register char ch=0;
    while(ch< '0'|| ch>'9')ch=gc();
    while(ch<='9' && ch >='0'){
        data=(data<<1) + (data<<3)+(ch&15);
        ch=gc();
    }
    return;
}
template <class R>
inline void write(R data){
    if(data>9)write(data/10);
    putchar(data%10+'0');
}
const int _ = 2e5+19,INF  = 2e9;
int n,m,dep[_],fa[_],to[_],nxt[_],fir[_],cnt_edge,cnt,dfn[_],fdfn[_],son[_],Size[_],top[_],Tree[_<<2],Lazy[_<<2];
int ans[_],Dep[_<<2];
inline void add(register int a,register int b){to[++cnt_edge]=b,nxt[cnt_edge]=fir[a],fir[a]=cnt_edge;}
struct me{
    int u,Ti,id;
}qq[_];
void Dfs(int now){
    dep[now]=dep[fa[now]]+1;
    for(register int i=fir[now];i;i=nxt[i]){
        if(to[i]==fa[now])continue;
        Dfs(to[i]);
        son[now] = Size[son[now]]<Size[to[i]]?to[i]:son[now];Size[now]+=Size[to[i]];
    }
    ++ Size[now];
}
void dfs(int now,int topf){
    dfn[now]=++cnt,fdfn[cnt]=now,top[now]=topf;
    if(son[now])dfs(son[now],topf);
    for(register int i=fir[now];i;i=nxt[i]){
        if(to[i]==son[now]||to[i]==fa[now])continue;
        dfs(to[i],to[i]);
    }
    return;
}
inline void update(register int qp){    Tree[qp]=max(Tree[qp<<1],Tree[qp<<1|1]);return;}
inline void pushdown(register int qp){
    if(Lazy[qp]!=-INF){
        Tree[qp<<1]=max(Tree[qp<<1],Dep[qp<<1]*2+Lazy[qp]);
        Lazy[qp<<1]=max(Lazy[qp<<1],Lazy[qp]);
        Tree[qp<<1|1]=max(Tree[qp<<1|1],Dep[qp<<1|1]*2+Lazy[qp]);
        Lazy[qp<<1|1]=max(Lazy[qp<<1|1],Lazy[qp]);
    }
    Lazy[qp]=-INF;return;
}
void Build(int l,int r,register int qp){
    Tree[qp]=Lazy[qp]=-INF;
    if(l==r){Lazy[qp]=Tree[qp]=-INF;Dep[qp]=dep[fdfn[l]];return;}
    int mid = l+r>>1;
    Build(l,mid,qp<<1),Build(mid+1,r,qp<<1|1);
    update(qp);
    Dep[qp]=max(Dep[qp<<1],Dep[qp<<1|1]);
    return;
}
int Query(const int l,const int r,int now_l,int now_r,int qp){
    if(l<=now_l&&r>=now_r){return Tree[qp];}
    int mid=now_l+now_r>>1;
    pushdown(qp);
    if(r<=mid) return Query(l,r,now_l,mid,qp<<1);
    if(l>mid) return Query(l,r,mid+1,now_r,qp<<1|1);
    return max( Query(l,r,now_l,mid,qp<<1),Query(l,r,mid+1,now_r,qp<<1|1)  );
}
void fix(const int l,const int r,int now_l,int now_r,int qp,const int zh){
    if(l<=now_l&&r>=now_r){Tree[qp]=max(Tree[qp],zh+2*Dep[qp]),Lazy[qp]=max(Lazy[qp],zh);return;}
    register int mid = now_l+now_r>>1;
    pushdown(qp);
    if(l<=mid) fix(l,r,now_l,mid,qp<<1,zh);
    if(r>mid) fix(l,r,mid+1,now_r,qp<<1|1,zh);
    update(qp);return;
}
inline int pre_query(register int a,register int ta,int ki){
    register int u=a,l=1,r,mid,Ans=0 ;
    while(u){
        if(Query(dfn[top[u]],dfn[u],1,n,1 )<= dep[a]+ta)u=fa[top[u]];
        else {
             l = dfn[u], r=dfn[top[u]],mid;
             register int uk=0;
            while(r<=l){
                mid = l+r>>1;
                if(Query(mid,l,1,n,1)>dep[a]+ta)r=mid+1,uk=mid;
                else l=mid-1;

            }
            if(uk)l=uk;

            break;
        }
    }
    l=fdfn[l],u=a;
    Ans=ta+(dep[a]-dep[l])*2;
    while(top[u]^top[l]){
        fix(dfn[top[u]],dfn[u],1,n,1,Ans-dep[a]);
        u=fa[top[u]];
    }
    fix(dfn[l],dfn[u],1,n,1,Ans-dep[a]);
    return Ans;
}
inline bool cmp1(register me a,register me b){
    if(dep[a.u]+a.Ti!=dep[b.u]+b.Ti)return dep[a.u]+a.Ti<dep[b.u]+b.Ti;
    return a.u<b.u;
}
int main(){
    freopen("message.in","r",stdin);
    freopen("message.out","w",stdout);
    read(n),read(m);++n;
    if(m==0)return 0;
    for(register int i=2,a;i<=n;++i){
        read(a);++a,add(a,i),add(i,a),fa[i]=a;
    }
    Dfs(1);dfs(1,1);Build(1,n,1);
    for(register int i=1;i<=m;++i)
        read(qq[i].u),read(qq[i].Ti),qq[i].id=i,qq[i].u++;
    sort(qq+1,qq+m+1,cmp1);
    for(register int i=1;i<=m;++i){
        ans[qq[i].id]=pre_query(qq[i].u,qq[i].Ti,i);
    }
    for(register int i=1;i<=m;++i){
        write(ans[i]);    
        puts("");
    }return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值