P1064 A 病毒 官方题解
Gioush OJ · P1064 A 病毒
【题意简述】
在一棵有根树上统计从每个非根节点出发、满足历史防疫限制并最终到达根的感染路线数。
【正解】
令 表示从 出发的合法路线数,;令
固定起点 ,枚举子树内节点 ,表示先只向下扩散到 ,再第一次向上感染。沿 维护
从 上跳后的落点深度范围为
若 ,贡献是对应祖先路径上的 ,用 的路径前缀差在 时间求出。对 的整棵子树求和即得 。
【正确性】
每条合法路线都存在唯一的“第一次向上感染”位置 。上式精确枚举其所有合法落点;防疫限制由路径最小值 同时约束,故不会遗漏或重复。由于第一次上跳一定到达 的严格祖先,按编号递增计算时所需状态均已求出。
【复杂度】
时间复杂度 ,空间复杂度 。
【参考代码】
/*Author:EhundateghDate:2026/8/17Name:cancer.cppYou steal,I kill.*/#include <vector>#include <cstdio>#include <cstring>#include <algorithm>#define MAXN 2010using namespace std;const int Mod=998244353;inline int Add(int a,int b){return a+b>=Mod?a+b-Mod:a+b;}inline int Del(int a,int b){return a-b<0?a-b+Mod:a-b;}int c,T,n,Fa[MAXN],Depth[MAXN],l[MAXN],r[MAXN],h[MAXN];int Dp[MAXN],Pre[MAXN],Path[MAXN],Ans;vector <int> Son[MAXN]; void DFS(int Now,int Limit){ Path[Depth[Now]]=Now; Limit=min(Limit,Depth[Now]-h[Now]-1); int Left=Depth[Now]-r[Now]; int Right=min(Depth[Now]-l[Now],Limit); if(Left<=Right){ int Temp=Pre[Path[Right]]; if(Left) Temp=Del(Temp,Pre[Path[Left-1]]); Ans=Add(Ans,Temp); } for(int i=0;i<(int)Son[Now].size();i++) DFS(Son[Now][i],Limit);} void Solve(){ scanf("%d",&n); for(int i=1;i<=n;i++) Son[i].clear(); Depth[1]=0;Dp[1]=Pre[1]=1; for(int i=2;i<=n;i++){ scanf("%d%d%d%d",&Fa[i],&l[i],&r[i],&h[i]); Depth[i]=Depth[Fa[i]]+1; Son[Fa[i]].push_back(i); } for(int i=2;i<=n;i++){ int Now=i; while(Now){Path[Depth[Now]]=Now;Now=Fa[Now];} Ans=0;DFS(i,n); Dp[i]=Ans; Pre[i]=Add(Pre[Fa[i]],Dp[i]); } for(int i=2;i<=n;i++) printf("%d%c",Dp[i]," \n"[i==n]);} int main(){ scanf("%d%d",&c,&T); while(T-->0) Solve(); return 0;}