洛谷P4216
算法:树状数组+离线思想+倍增LCA
时间复杂度:
第一问显然,为两者距离加
第二问,设当前有一个询问
不难得出,这个点需要满足两个条件
对第二个条件进行转化,变为
结合第一个条件,不难得出询问的本质:问
先将问题简化,考虑不带修(即最开始每个点都有一个时间戳)。
转化问题,设
考虑离线转化,将询问记录在每个点上,然后使用dfs遍历,遍历到一个点的时候同时处理询问即可。
具体来说,用一个数组
在dfs的时候,遍历到当前点
事实上,这种树上离线的思想非常重要,各位可以记下来。另有一道题:NOIP天天爱跑步,用了类似的思想,感兴趣的可以去挑战一下
回到本题,带修怎么办?
观察
那如果
不用想的太复杂,此时的答案显然就跟第一问的答案相同。
code
#define ll long long
#define ull unsigned long long
using namespace std;
const int N=2e5+10;
int n,m,q;
int dep[N],f[N][25],dis[N],ans[N],col[N],fa[N];
int End[N<<1],Last[N],Next[N<<1];
int c[N];
struct Node
{
int dif,id,mark;
}temp;
vector<Node> query[N];
int read()
{
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-f;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-48;s=getchar();}
return x*f;
}
int tot;
void add(int x,int y)
{
End[++tot]=y,Next[tot]=Last[x],Last[x]=tot;
}
void deal_first(int x,int fa)
{
dep[x]=dep[fa]+1;
for(int i=1;i<=20;i++)
f[x][i]=f[f[x][i-1]][i-1];
for(int i=Last[x];i;i=Next[i])
{
int u=End[i];
if(u==fa) continue;
f[u][0]=x;
deal_first(u,x);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=20;i>=0;i--)
{
if(dep[f[x][i]]>=dep[y])
x=f[x][i];
if(x==y) return x;
if(dep[x]==dep[y]) break;
}
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])
{
x=f[x][i];
y=f[y][i];
}
return f[x][0];
}
void modify(int x,int d)
{
for(;x<=q;x+=x&-x)
c[x]+=d;
}
int ask(int x)
{
int res=0;
for(;x>=1;x-=x&-x)
res+=c[x];
return res;
}
void dfs(int x,int fa)
{
if(col[x])
modify(col[x],1);
for(int i=0;i<query[x].size();i++)
ans[query[x][i].id]+=ask(query[x][i].dif-1)*query[x][i].mark;
for(int i=Last[x];i;i=Next[i])
{
int u=End[i];
if(u==fa) continue;
dfs(u,x);
}
if(col[x])
modify(col[x],-1);
}
int main()
{
int cnt=0,Root;
n=read();
for(int i=1,u;i<=n;i++)
{
u=read();
fa[i]=u;
if(!u)
{
Root=i;
continue;
}
add(u,i);
add(i,u);
}
deal_first(Root,0);
q=read();
int x,y,c;
for(int i=1;i<=q;i++)
{
int op=read();
if(op==1)
{
cnt++;
x=read(),y=read();
c=read();
temp.id=cnt;
temp.dif=i-c;
temp.mark=1;
query[x].push_back(temp);
query[y].push_back(temp);
temp.mark=-1;
int lca=LCA(x,y);
query[lca].push_back(temp);
query[fa[lca]].push_back(temp);
dis[cnt]=dep[x]+dep[y]-(dep[lca]<<1);
}
else col[read()]=i;
}
dfs(Root,0);
for(int i=1;i<=cnt;i++)
printf("%d %d\n",dis[i]+1,ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构