[清华集训2017] Hello World!
Hello world!
题目背景
不远的一年前,小 V 还是一名清华集训的选手,坐在机房里为他已如风中残烛的OI 生涯做最后的挣扎。而如今,他已成为了一名光荣的出题人。他感到非常激动,不禁感叹道: “Hello world!”。
题目描述
小 V 有 \(n\) 道题,他的题都非常毒瘤,所以关爱选手的 ufozgg 打算削弱这些题。为了逃避削弱,小 V 把他的毒瘤题都藏到了一棵 \(n\) 个节点的树里(节点编号从 \(1\) 至 \(n\)),这棵树上的所有节点与小 V 的所有题一一对应。小 V 的每一道题都有一个毒瘤值,节点 \(i\) (表示标号为 \(i\) 的树上节点,下同)对应的题的毒瘤值为 \(a_i\)。
魔法师小 V 为了保护他的题目,对这棵树施了魔法,这样一来,任何人想要一探这棵树的究竟,都必须在上面做跳跃操作。每一次跳跃操作包含一个起点 \(s\) 、一个终点 \(t\) 和一个步频 \(k\) ,这表示跳跃者会从 \(s\) 出发,在树上沿着简单路径多次跳跃到达 \(t\) ,每次跳跃,如果从当前点到 \(t\) 的最短路长度不超过 \(k\) ,那么跳跃者就会直接跳到 \(t\) ,否则跳跃者就会沿着最短路跳过恰好 \(k\) 条边。
既然小 V 把题藏在了树里, ufozgg 就不能直接削弱题目了。他就必须在树上跳跃,边跳跃边削弱题目。 ufozgg 每次跳跃经过一个节点(包括起点 \(s\) ,当 \(s = t\) 的时候也是如此),就会把该节点上的题目的毒瘤值开根并向下取整:即如果他经过了节点 \(i\),他就会使 \(a_i = \lfloor \sqrt{a_i} \rfloor\)。这种操作我们称为削弱操作。
ufozgg 还会不时地希望知道他对题目的削弱程度。因此,他在一些跳跃操作中会放弃对题目的削弱,转而统计该次跳跃经过节点的题目毒瘤值总和。这种操作我们称为统计操作。
吃瓜群众绿绿对小 V 的毒瘤题和 ufozgg 的削弱计划常感兴趣。他现在想知道ufozgg 每次做统计操作时得到的结果。你能帮帮他吗?
输入格式
从文件 helloworld.in 中读入数据。
输入的第一行一个正整数 \(n\) ,表示树的节点数。
接下来一行 \(n\) 个用空格隔开的正整数 \(a_1, a_2, \ldots, a_n\),依次描述每个节点上题目的毒瘤值。
接下来 \(n − 1\) 行,描述这棵树。每行 \(2\) 个正整数 \(u, v\) ,描述一条树上的边 \((u, v)\) 。(保证 \(1 \leq u, v \leq n\) ,保证这 \(n − 1\) 条边构成了一棵树)
接下来一行一个正整数 \(Q\) ,表示 ufozgg 的操作总数。
接下来 \(Q\) 行按 ufozgg 执行操作的先后顺序依次描述每个操作,每行 \(4\) 个用空格隔开的整数 \(op, s, t, k\) ,表示 ufozgg 此次跳跃的起点为 \(s\) ,终点为 \(t\) ,步频为 \(k\) 。如果 \(op = 0\) ,表示这是一次削弱操作;如果 \(op = 1\) ,表示这是一次统计操作。
输出格式
对于每个统计操作,输出一行一个整数,表示此次统计操作统计到的所有题的毒瘤值总和。
样例 #1
样例输入 #1
5
1 2 3 4 5
1 2
2 3
3 4
2 5
5
1 1 4 1
1 1 4 2
0 1 5 2
1 2 4 5
1 1 5 1
样例输出 #1
10
8 6 5
提示
对于 \(100\%\) 的数据,\(n≤50000\), \(Q≤400000\), \(1\leq ai\leq 10^{13}\)。
对于所有的操作保证 \(0\leq op\leq 1\),\(1\leq s,t,k\leq n\)。
引入:
维护一个数据结构支持单点修改,链求和。这个是可以不用树剖的。可以维护一个点到根之和,修改时相当于子树加,链求和可以差分。所以用树状数组维护 dfs 序就可以了。
走 \(k\),步,一个很经典的根号分治的线索。
预处理长剖 \(k\) 级祖先,所有询问都可以在 \(O(\frac{nQ}k)\) 的复杂度以内完成。可以先把 \(t\) 向上跳 \(dis(s,t)\bmod k\) 步,然后 \(s\) 和 \(t\) 一起往上 \(k\) 步的跳。
然后考虑把 \(k\) 比较小的情况维护出来。先看修改,首先一个点开根 \(\log\log a\) 次之后就会变成 1。可以用并查集维护一个点向上跳 \(k\) 步,第一个不为 1 的在哪里。然后我们就可以用引入的内容维护所有的 \(k\) 步,维护 \(dep mod k\) 这条链之和。查询时直接询问链就行了。
如果对 \(B\) 以内的 \(k\) 这样预处理复杂度就是 \(nB\log\log A\log n\)
然后
B 取 \(\sqrt{\frac Q{\log\log a\log n}}\) 时,复杂度取到最优值。但是实测时 B 取 25 最好(常数小?)
#include<bits/stdc++.h>
using namespace std;
const int N=50005,B=25;
typedef long long LL;
int n,fa[B][N],f[N][20],ln[N],son[N],q,in[B][N],out[B][N],top[N],dep[N],lg[N],c;
LL ans,a[N];
vector<int>u[N],d[N],g[N];
vector<LL>tr[B][B];
void upd(int k,int p,int x,LL y)
{
for(int i=x;i<tr[k][p].size();i+=i&-i)
tr[k][p][i]+=y;
}
void sou(int x,int y)
{
f[x][0]=y;
dep[x]=dep[y]+1;
for(int i=1;i<B;i++)
{
in[i][x]=tr[i][dep[x]%i].size();
tr[i][dep[x]%i].push_back(0);
}
for(int i=1;i<20;i++)
f[x][i]=f[f[x][i-1]][i-1];
for(int v:g[x])
{
if(v==y)
continue;
sou(v,x);
if(ln[v]+1>ln[x])
ln[x]=ln[v]+1,son[x]=v;
}
for(int i=1;i<B;i++)
out[i][x]=tr[i][dep[x]%i].size();
}
void dfs(int x,int y,int tp)
{
d[tp].push_back(x);
if(son[x])
dfs(son[x],x,tp);
top[x]=tp;
for(int v:g[x])
if(v^y&&v^son[x])
dfs(v,x,v);
if(x==tp)
{
int y=x;
for(int i=0;i<=ln[x]&&y;i++)
{
u[x].push_back(y);
y=f[y][0];
}
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
while(dep[x]>dep[y])
x=f[x][lg[dep[x]-dep[y]]];
if(x==y)
return x;
for(int i=19;~i;--i)
if(f[x][i]^f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int ask(int x,int k)
{
if(!k)
return x;
int p=x,q=lg[k];
assert(dep[x]-k>0);
x=f[x][lg[k]];
assert(x);
k-=1<<lg[k];
if(dep[x]-dep[top[x]]<=k)
return u[top[x]][k-dep[x]+dep[top[x]]];
return d[top[x]][dep[x]-dep[top[x]]-k];
}
int find(int k,int x)
{
if(fa[k][x]==x)
return x;
return fa[k][x]=find(k,fa[k][x]);
}
void upd(int x)
{
if(a[x]==1)
return;
LL t=sqrt(a[x]);
assert(t*t<=a[x]);
while((t+1)*(t+1)<a[x])
++t;
for(int i=1;i<B;i++)
{
upd(i,dep[x]%i,in[i][x],t-a[x]);
upd(i,dep[x]%i,out[i][x],a[x]-t);
}
a[x]=t;
if(a[x]==1)
for(int i=1;i<B&&i<dep[x];i++)
fa[i][x]=find(i,ask(x,i));
}
LL query(int k,int x)
{
int d=dep[x]%k,p=in[k][x];
LL ans=0;
for(;p;p-=p&-p)
ans+=tr[k][d][p];
return ans;
}
int main()
{
for(int i=2;i<N;i++)
lg[i]=lg[i>>1]+1;
for(int i=0;i<B;i++)
{
for(int j=0;j<B;j++)
tr[i][j].push_back(0);
for(int j=1;j<N;j++)
fa[i][j]=j;
}
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",a+i);
for(int i=1,u,v;i<n;i++)
scanf("%d%d",&u,&v),g[u].push_back(v),g[v].push_back(u);
sou(1,0);
dfs(1,0,1);
for(int i=1;i<B;i++)
for(int j=1;j<=n;j++)
if(a[j]==1&&i<dep[j])
fa[i][j]=find(i,ask(j,i));
for(int i=1;i<B;i++)
for(int j=1;j<=n;j++)
upd(i,dep[j]%i,in[i][j],a[j]),upd(i,dep[j]%i,out[i][j],-a[j]);
scanf("%d",&q);
while(q--)
{
int op,s,t,k,d;
scanf("%d%d%d%d",&op,&s,&t,&k);
d=lca(s,t);
int g=(dep[s]+dep[t]-2*dep[d])%k;
if(op)
{
ans=a[t]+a[s];
if(g&&dep[t]-g>=dep[d])
{
t=ask(t,g);
ans+=a[t];
}
if(k>=B)
{
while(dep[s]-k>=dep[d])
{
s=ask(s,k);
ans+=a[s];
}
while(dep[t]-k>=dep[d])
{
t=ask(t,k);
ans+=a[t];
}
}
else
{
if(dep[s]-k>=dep[d])
{
int u=(dep[d]-dep[s]%k+k)%k;
if(!u)
u=k;
ans+=query(k,ask(s,k));
if(dep[d]>u)
ans-=query(k,ask(d,u));
}
if(dep[t]-k>=dep[d])
{
int u=(dep[d]-dep[t]%k+k)%k;
if(!u)
u=k;
ans+=query(k,ask(t,k));
if(dep[d]>u)
ans-=query(k,ask(d,u));
}
}
if(dep[d]%k==dep[s]%k)
ans-=a[d];
printf("%lld\n",ans);
++c;
}
else
{
if(g&&dep[t]-g>dep[d])
{
upd(t);
t=ask(t,g);
}
if(d^t)
upd(t);
if(d^s)
upd(s);
if(k>=B)
{
while(dep[s]-k>dep[d])
{
s=ask(s,k);
upd(s);
}
while(dep[t]-k>dep[d])
{
t=ask(t,k);
upd(t);
}
}
else
{
while(dep[s]-k>dep[d])
{
s=find(k,ask(s,k));
if(dep[s]>dep[d])
upd(s);
}
while(dep[t]-k>dep[d])
{
t=find(k,ask(t,k));
if(dep[t]>dep[d])
upd(t);
}
}
if(dep[s]%k==dep[d]%k||t==d)
upd(d);
}
}
}
[清华集训2017] Hello World!的更多相关文章
- Loj #2331. 「清华集训 2017」某位歌姬的故事
Loj #2331. 「清华集训 2017」某位歌姬的故事 IA 是一名会唱歌的女孩子. IOI2018 就要来了,IA 决定给参赛选手们写一首歌,以表达美好的祝愿.这首歌一共有 \(n\) 个音符, ...
- Loj #2324. 「清华集训 2017」小 Y 和二叉树
Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...
- Loj #2321. 「清华集训 2017」无限之环
Loj #2321. 「清华集训 2017」无限之环 曾经有一款流行的游戏,叫做 *Infinity Loop***,先来简单的介绍一下这个游戏: 游戏在一个 \(n \times m\) 的网格状棋 ...
- Loj 2320.「清华集训 2017」生成树计数
Loj 2320.「清华集训 2017」生成树计数 题目描述 在一个 \(s\) 个点的图中,存在 \(s-n\) 条边,使图中形成了 \(n\) 个连通块,第 \(i\) 个连通块中有 \(a_i\ ...
- 【UOJ#340】【清华集训2017】小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划)
[UOJ#340][清华集训2017]小 Y 和恐怖的奴隶主(矩阵快速幂,动态规划) 题面 UOJ 洛谷 题解 考虑如何暴力\(dp\). 设\(f[i][a][b][c]\)表示当前到了第\(i\) ...
- loj #2325. 「清华集训 2017」小Y和恐怖的奴隶主
#2325. 「清华集训 2017」小Y和恐怖的奴隶主 内存限制:256 MiB时间限制:2000 ms标准输入输出 题目类型:传统评测方式:文本比较 题目描述 "A fight? Co ...
- [LOJ#2330]「清华集训 2017」榕树之心
[LOJ#2330]「清华集训 2017」榕树之心 试题描述 深秋.冷风吹散了最后一丝夏日的暑气,也吹落了榕树脚下灌木丛的叶子.相识数年的Evan和Lyra再次回到了小时候见面的茂盛榕树之下.小溪依旧 ...
- [LOJ#2329]「清华集训 2017」我的生命已如风中残烛
[LOJ#2329]「清华集训 2017」我的生命已如风中残烛 试题描述 九条可怜是一个贪玩的女孩子. 这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\ ...
- [LOJ#2328]「清华集训 2017」避难所
[LOJ#2328]「清华集训 2017」避难所 试题描述 "B君啊,你当年的伙伴都不在北京了,为什么你还在北京呢?" "大概是因为出了一些事故吧,否则这道题就不叫避难所 ...
- [LOJ#2327]「清华集训 2017」福若格斯
[LOJ#2327]「清华集训 2017」福若格斯 试题描述 小d是4xx9小游戏高手. 有一天,小d发现了一个很经典的小游戏:跳青蛙. 游戏在一个 \(5\) 个格子的棋盘上进行.在游戏的一开始,最 ...
随机推荐
- 带你读论文丨S&P21 Survivalism: Living-Off-The-Land 经典离地攻击
本文分享自华为云社区<[论文阅读] (21)S&P21 Survivalism: Living-Off-The-Land经典离地攻击>,作者: eastmount . 摘要 随着恶 ...
- PRACK消息
概述 PRACK消息是sip协议的扩展,在RFC3262中定义,标准的名称是sip协议中的可靠临时响应. 本文简单介绍标准中对PRACK消息流程的描述,以及fs配置PRACK的方式. 环境 cento ...
- 用一个示例来学习DockerFile
在Docker的世界里,我们经常会听到Dockerfile这个词.那么,什么是Dockerfile?它如何工作?本文将简要介绍Dockerfile的基本概念,原理以及一些常用的Dockerfile命令 ...
- SpringBoot拦截器和动态代理有什么区别?
在 Spring Boot 中,拦截器和动态代理都是用来实现功能增强的,所以在很多时候,有人会认为拦截器的底层是通过动态代理实现的,所以本文就来盘点一下他们两的区别,以及拦截器的底层实现. 1.拦截器 ...
- Skynet:Debug Console的扩展
起因 最近上线服务器遇到了一些问题,上个月CPU暴涨的问题,那个经查验是死循环导致endless loop了. 这周又遇到了mem占用达到96%的问题,在debug console里调用了gc之后,跌 ...
- Python Flask 上下文管理源码分析
Python Flask 上下文管理源码分析 前言 Flask 上下文管理可以说是 Flask 非常具有特色的设计,它总共可分为 2 个大的方向: 应用上下文管理:通过 current_app 即可拿 ...
- Arrays.asList():使用指南
Arrays.asList() 是一个 Java 的静态方法,它可以把一个数组或者多个参数转换成一个 List 集合.这个方法可以作为数组和集合之间的桥梁,方便我们使用集合的一些方法和特性.本文将介绍 ...
- CAP项目集成带身份和证书验证的MongoDB
大家好,我是Edison. 最近,在使用CAP事件总线时,碰到了这样一个需求:微服务采用的是MongoDB,而且还是带身份验证 和 SSL根证书验证的.由于目前网上能找到的资料,都是不带身份验证的Mo ...
- Java 多线程线程池的工作流程
1.在创建了线程池后,等待提交过来的任务请求. 2.当调用execute()方法添加一个请求任务时,线程池就会做如下判断: 2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程 ...
- 前端三件套系例之JS——JavaScript内置方法
文章目录 1.Number 1-1 属性 1-2 方法 2.String 2-1 属性 2-2 方法 2-3 代码 3Array 3-1 创建数组 3-2 数组特点 3-3 数组的遍历(迭代) 34 ...