QOJ 5020. 举办乘凉州喵,举办乘凉州谢谢喵

飞天数据结构。

思路

设 \(f[u][k]\) 为 \(u\) 子树内距离 \(u\) 小于等于 \(k\) 的点的个数,\(g[u][k]\) 为 \(u\) 的轻子树中距离小于等于 \(k\) 的点的个数。

对于一条路径 \((x,y)\) 来说,设其 \(Lca\) 为 \(z\) 我们不妨看成是一条 \(x\to z\) 和一条 \(y\to z\) 的路径。

这种祖先关系的路径,如果是一条重链上那么答案是 \(\sum_{u\in(x\to z)} g[u][k]+f[hso_u][k-1]-f[z][k]+|x\to z|\)(\(hso_u\) 是 \(u\) 的重儿子)加上距离 \(z\) 在 \(k\) 以内的点的个数。

如果是若干条重链拼在一起,先加上链上的 \(g[u][k]\),对于一条轻边 \((u,v)\),设父亲是 \(v\),那么需要减 \(f[u][k]\) 加 \(f[hso_v][k-1]\)。

跳重链,把所有 \(f,g\) 值离线到点。

对于 \(f\) 值,可以视作一个二维偏序,不过这样就 \(q\log^3 n\) 了。更好的方法是使用树状树组存深度个数,遍历到该点时查询一次深度不大于 \(k\) 的个数,与遍历出该点时深度不大于 \(k\) 的个数作差。

对于 \(g\) 值,每到一个点暴力的跑轻子树,将轻子树与其的距离加入树状树组 \(G\),此时 \(G_k\) 是 \(g_{u,k}\),不清空遍历儿子,这样就是根到当前节点的 \(g\) 的和,类似树上查询深度的方式即可解决 \(g\) 的查询。

对于求一个点距离不超过 \(k\) 的点的个数,是点分治的经典问题。

CODE

#include<bits/stdc++.h>
using namespace std; #define N 200000 const int maxn=2e5+5; int q,n;
int ans[maxn]; struct treearray
{
int ts[maxn];
inline int lowbit(int x){return x&(-x);}
inline void updata(int x,int y){for(;x<=N;x+=lowbit(x)) ts[x]+=y;}
inline int getsum(int x){int sum=0;for(;x;x-=lowbit(x)) sum+=ts[x];return sum;}
}Sdep,G,Sdis,Tdis;
struct Edge
{
int tot;
int head[maxn];
struct edgenode{int to,nxt;}edge[maxn*2];
inline void add(int x,int y)
{
tot++;
edge[tot].to=y;
edge[tot].nxt=head[x];
head[x]=tot;
}
}T;
struct QRY{int fx,d,id;};
struct QRY_f{int fx,l,r,id;}; int hso[maxn],dep[maxn],fa[maxn],siz[maxn],tp[maxn]; vector<QRY>gvec[maxn],dvec[maxn];
vector<QRY_f>fvec[maxn]; inline void dfs(int u,int f)
{
dep[u]=dep[f]+1;fa[u]=f;siz[u]=1;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
dfs(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[hso[u]]) hso[u]=v;
}
}
inline void dfs2(int u,int t)
{
tp[u]=t;
if(hso[u]) dfs2(hso[u],t);
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==fa[u]||v==hso[u]) continue;
dfs2(v,v);
}
}
inline int Lca(int u,int v)
{
while(tp[u]!=tp[v])
{
if(dep[tp[u]]<dep[tp[v]]) swap(u,v);
u=fa[tp[u]];
}
if(dep[u]<dep[v]) swap(u,v);
return v;
} namespace solvefg
{
inline void dfsf(int u,int f)
{
for(auto v:fvec[u])
ans[v.id]-=(Sdep.getsum(v.r)-Sdep.getsum(v.l-1))*v.fx;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
dfsf(v,u);
}
Sdep.updata(dep[u],1);
for(auto v:fvec[u])
ans[v.id]+=(Sdep.getsum(v.r)-Sdep.getsum(v.l-1))*v.fx;
}
inline void dfs_cnt(int u,int f,int dis,int val)
{
G.updata(dis,val);
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
dfs_cnt(v,u,dis+1,val);
}
}
inline void dfsg(int u,int f)
{
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f||v==hso[u]) continue;
dfs_cnt(v,u,1,1);
}
for(auto v:gvec[u]) ans[v.id]+=G.getsum(v.d)*v.fx;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f) continue;
dfsg(v,u);
}
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(v==f||v==hso[u]) continue;
dfs_cnt(v,u,1,-1);
}
}
inline void goup(int u,int v,int d,int id)
{
ans[id]+=dep[u]-dep[v]+1;
gvec[u].push_back({1,d,id});gvec[fa[v]].push_back({-1,d,id});
fvec[hso[u]].push_back({1,dep[u],min(dep[u]+d,n),id});
while(tp[u]!=tp[v])
{
u=tp[u];
fvec[u].push_back({-1,dep[fa[u]],min(dep[fa[u]]+d,n),id});
u=fa[u];
fvec[hso[u]].push_back({1,dep[u],min(dep[u]+d,n),id});
}
}
}
namespace treediv
{
int siz[maxn];
bool book[maxn],cut[maxn];
inline void dfs_siz(int u)
{
book[u]=true;siz[u]=1;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(book[v]||cut[v]) continue;
dfs_siz(v);siz[u]+=siz[v];
}
book[u]=false;
}
inline int dfs_rt(int u,const int tot)
{
book[u]=true;int ret=u;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(book[v]||cut[v]) continue;
if(siz[v]*2>=tot) {ret=dfs_rt(v,tot);break;}
}
book[u]=false;return ret;
}
inline void dfs_cnt(int u,int dis,int val)
{
Sdis.updata(dis+1,val);
book[u]=true;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(book[v]||cut[v]) continue;
dfs_cnt(v,dis+1,val);
}
book[u]=false;
}
inline void dfs_ept(int u,int dis,int val)
{
Tdis.updata(dis+1,val);
book[u]=true;
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(book[v]||cut[v]) continue;
dfs_ept(v,dis+1,val);
}
book[u]=false;
}
inline void calc(int u,int dis)
{
for(auto i:dvec[u])
{
if(i.d-dis+1<0) continue;
ans[i.id]+=Sdis.getsum(i.d-dis+1)-Tdis.getsum(i.d-dis+1);
}
}
inline void dfs_calc(int u,int dis)
{
book[u]=true;calc(u,dis);
for(int i=T.head[u];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(book[v]||cut[v]) continue;
dfs_calc(v,dis+1);
}
book[u]=false;
}
inline void dfs(int u)
{
dfs_siz(u);int g=dfs_rt(u,siz[u]);cut[g]=true;
dfs_cnt(g,0,1);
calc(g,0);
for(int i=T.head[g];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(cut[v]) continue;
dfs_ept(v,1,1);
dfs_calc(v,1);
dfs_ept(v,1,-1);
}
dfs_cnt(g,0,-1);
for(int i=T.head[g];i;i=T.edge[i].nxt)
{
int v=T.edge[i].to;
if(cut[v]) continue;
dfs(v);
}
}
} int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
T.add(u,v),T.add(v,u);
}
dfs(1,0),dfs2(1,1);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
int u,v,d;
scanf("%d%d%d",&u,&v,&d);
int lca=Lca(u,v);
fvec[lca].push_back({-2,dep[lca],min(dep[lca]+d,n),i});
dvec[lca].push_back({1,d,i});
solvefg::goup(u,lca,d,i);solvefg::goup(v,lca,d,i);
}
solvefg::dfsf(1,0);
solvefg::dfsg(1,0);
treediv::dfs(1);
for(int i=1;i<=q;i++) printf("%d\n",ans[i]);
}

QOJ 5020. 举办乘凉州喵,举办乘凉州谢谢喵的更多相关文章

  1. 【SIGGRAPH 2015】【巫师3 狂猎 The Witcher 3: Wild Hunt 】顶级的开放世界游戏的实现技术。

    [SIGGRAPH 2015][巫师3 狂猎 The Witcher 3: Wild Hunt ]顶级的开放世界游戏的实现技术 作者:西川善司 日文链接  http://www.4gamer.net/ ...

  2. 成都OpenPart——DevOps专场活动参与感

    今天下午去参加了成都OpenPart——DevOps专场,感觉很好. 题外话: 回想一下,工作将近四年了,这是第一次参加类似的活动.自从结婚带了小孩以后,就基本上每个周末奔波工作和家里两个城市之间,这 ...

  3. Asterix and Obelix

    uva10246:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&am ...

  4. 17秋 软件工程 团队第五次作业 Alpha 测试报告

    用户反馈博客:17秋 软件工程 团队第五次作业 Alpha 用户反馈 团队项目软件的总体测试计划 测试模块: 用户登录 部门信息模块 活动模块 部员管理模块 短信通知模块 测试计划: 注:测试结果Y代 ...

  5. Vijos——T1406 拉力赛

    https://vijos.org/p/1460 描述 车展结束后,游乐园决定举办一次盛大的山道拉力赛,平平和韵韵自然也要来参加大赛. 赛场上共有n个连通的计时点,n-1条赛道(构成了一棵树).每个计 ...

  6. O准备如何苟进复赛圈?华为软挑开挂指南(附赛题预测)

    事先声明,这不是华为软挑的软广,我也不是海军. 这篇文章纯粹是心血来潮,原因是去年上传到github的参赛代码,前几天又有两个人star和fork了. 记得star热潮还是去年4月复赛刚结束的那几天, ...

  7. 年度开源盛会 ApacheCon 首发中文盛宴来临,欢迎报名!

    ApacheCon 是久负盛名的开源盛宴,为开源界备受关注的会议之一,也是开源运动早期的知名活动之一,其最早的一期要追溯 1998 年,也是在这一届上,开发 HTTPD 服务的开发者们欢聚一堂,并决定 ...

  8. 应用OpenMP的一个简单的设计模式

    小喵的唠叨话:最近很久没写博客了,一是因为之前写的LSoftmax后馈一直没有成功,所以在等作者的源码.二是最近没什么想写的东西.前两天,在预处理图片的时候,发现处理200w张图片,跑了一晚上也才处理 ...

  9. python---面对对象的三大特征

    一.三大特征 面对对象的三大特征:继承.多态和封装,继承在面向对象类的创建中非常常见. 1.继承 为什么会有继承,当我们要创建一个新类的时候,发现他有很多属性或者反法都和我们另一个类的方法相同,这时我 ...

  10. python第二十三天-----Tornado

    Tornado是一个轻量级完整的web框架,在Linux系统下它会使用epoll,是一个异步非阻塞的web服务器框架,对于实时应用来说很理想,想想同是异步非阻塞的nginx的残暴程度就知道了 1.路由 ...

随机推荐

  1. PyTorch从入门到放弃之张量模块

    目录 张量的数据类型 torch.rand()函数 torch.randn()函数 torch.normal()函数 torch.linspace()函数 torch.manual_seed()函数 ...

  2. Seata 四大模式详解

    分布式事务 参考文章: 分布式事务实战方案汇总 https://www.cnblogs.com/yizhiamumu/p/16625677.html 分布式事务原理及解决方案案例https://www ...

  3. R-Adapter:零样本模型微调新突破,提升鲁棒性与泛化能力 | ECCV 2024

    大规模图像-文本预训练模型实现了零样本分类,并在不同数据分布下提供了一致的准确性.然而,这些模型在下游任务中通常需要微调优化,这会降低对于超出分布范围的数据的泛化能力,并需要大量的计算资源.论文提出新 ...

  4. 以太坊Rollup方案之 arbitrum(1)

    什么是Rollup? 以太坊的Rollup扩容是一种Layer 2(第二层)扩容解决方案,旨在提高以太坊区块链的交易吞吐量和性能.它通过将大量的交易数据转移到以太坊区块链之外的第二层网络来实现这一目标 ...

  5. CPP在内网穿透技术的思考

    概述 内网穿透是一种技术,用于在私有局域网(LAN)中的设备与外部网络(如互联网)之间建立通信通道,使得外部设备可以访问内网中的服务.由于内网设备通常位于防火墙或 NAT(网络地址转换)设备之后,外部 ...

  6. Angular 学习笔记 work with excel (导出 excel)

    更新: 2020-04-15 补上 read excel 先用 file reader 把文件变成 buffer 然后调用 exceljs 就可以了, 它很聪明的哦, date number, boo ...

  7. GPUStack 0.2:开箱即用的分布式推理、CPU推理和调度策略

    GPUStack 是一个专为运行大语言模型(LLM)设计的开源 GPU 集群管理器,旨在支持基于任何品牌的异构 GPU 构建统一管理的算力集群,无论这些 GPU 运行在 Apple Mac.Windo ...

  8. SpringBoot——基础配置

    基础配置 配置格式 SpringBoot提供了多种属性配置方法 application.properties server.port=80 application.yml server: port: ...

  9. SpringMVC——SSM整合——项目异常处理

    项目异常处理 项目异常分类 业务异常 不规范的用户行为产生的异常    规范的用户行为产生的异常    系统异常 项目运行过程中可预计且无法避免的异常    其他异常 编程人员未预期到的异常    项 ...

  10. SXYZ-6.27专题比赛

    好的,现在正式定义今天的比赛为一场伤心的比赛. ↑这张图片首先能说明一些问题,但这并不是关键. ↓这才是伤心的关键 ↑第一题文件输入输入爆 ↑第二题文件名直接爆 评语,一个比一个离谱! 然后只是很简单 ...