@description@

给定一棵 n 个点的树,树上每条边的长度都为 1 ,第 i 个点的权值为 a[i]。

Byteasar 会按照某个 1 到 n 的全排列 b 走 n-1 次,第 i 次他会从 b[i] 点走到 b[i+1] 点,并且这一次的步伐大小为 c[i]。

对于一次行走,假设起点为 x,终点为 y,步伐为 k,那么 Byteasar 会从 x 开始,每步往前走 k 步,如果最后不足 k 步就能到达 y,那么他会一步走到 y。

请帮助 Byteasar 统计出每一次行走时经过的所有点的权值和。

input

第一行包含一个正整数 n(2<=n<=50000)。表示节点的个数。

第二行包含 n 个正整数,其中第 i 个数为 ai,分别表示每个点的权值。

接下来 n-1 行,每行包含两个正整数 u, v(1<=u,v<=n),表示u与v之间有一条边。

接下来一行包含 n 个互不相同的正整数,其中第i个数为 bi,表示行走路线。

接下来一行包含 n-1 个正整数,其中第 i 个数为 ci,表示每次行走的步伐大小。

output

包含n-1行,每行一个正整数,依次输出每次行走时经过的所有点的权值和

sample input

5

1 2 3 4 5

1 2

2 3

3 4

3 5

4 1 5 2 3

1 3 1 1

sample output

10

6

10

5

@solution@

通过一些简单的树上差分,可以转换为这样一个问题:

若干次询问,第 i 次询问某个点 xi 到根的路径上深度 mod mi = ci 的点的权值和。

一个比较经典的模型。我们将 mi 分两类处理:

(1)\(mi \le \sqrt n\),此时 (mi, ci) 这样一个二元组的数量只有 O(n) 个。我们将这些二元组暴力存下来,每一次加入一个点用 \(O(\sqrt n)\) 的时间去更新。

(2)\(mi > \sqrt n\),此时满足要求的点数量为 \(O(\frac{n}{mi}) = O(\sqrt n)\),暴力将这些点找出来即可。

离线处理,采用 dfs 去统计信息并回答询问。

时间复杂度,第一类为 \(O(n) + O(n\sqrt n)\),第二类为 \(O(n\sqrt n) + O(n)\)。其中前者为询问复杂度,后者为统计信息的复杂度。

总时间复杂度 \(O(n\sqrt n)\)。

@accepted code@

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN = 50000;
const int SQRT = 250;
struct edge{
int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = &edges[0];
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
}
int dep[MAXN + 5], fa[20][MAXN + 5];
void dfs(int rt, int pre) {
dep[rt] = dep[pre] + 1, fa[0][rt] = pre;
for(int i=1;i<20;i++)
fa[i][rt] = fa[i-1][fa[i-1][rt]];
for(edge *p=adj[rt];p;p=p->nxt)
if( p->to != pre ) dfs(p->to, rt);
}
int lca(int u, int v) {
if( dep[u] < dep[v] ) swap(u, v);
for(int i=19;i>=0;i--)
if( dep[fa[i][u]] >= dep[v] )
u = fa[i][u];
if( u == v ) return u;
for(int i=19;i>=0;i--)
if( fa[i][u] != fa[i][v] )
u = fa[i][u], v = fa[i][v];
return fa[0][u];
}
struct query{
int m, r, f;
int num;
query(int _m=0, int _r=0, int _f=0, int _n=0):m(_m), r(_r), f(_f), num(_n){}
};
vector<query>qry[MAXN + 5];
int a[MAXN + 5], b[MAXN + 5], c[MAXN + 5];
int ans[MAXN + 5], arr[MAXN + 5];
int res[SQRT + 5][SQRT + 5];
void dfs2(int x) {
arr[dep[x]] = a[x];
for(int i=1;i<=SQRT;i++)
res[i][dep[x]%i] += a[x];
for(int i=0;i<qry[x].size();i++) {
if( qry[x][i].m <= SQRT ) {
ans[qry[x][i].num] += qry[x][i].f*res[qry[x][i].m][qry[x][i].r];
//printf("%d %d %d\n", qry[x][i].num, x, qry[x][i].f*res[qry[x][i].m][qry[x][i].r]);
}
else {
for(int j=qry[x][i].r;j<=dep[x];j+=qry[x][i].m)
ans[qry[x][i].num] += qry[x][i].f*arr[j];
}
}
for(edge *p=adj[x];p;p=p->nxt)
dfs2(p->to);
for(int i=1;i<=SQRT;i++)
res[i][dep[x]%i] -= a[x];
}
int main() {
int n; scanf("%d", &n);
for(int i=1;i<=n;i++) scanf("%d", &a[i]);
for(int i=1;i<n;i++) {
int u, v; scanf("%d%d", &u, &v);
addedge(u, v);
}
dfs(1, 0);
for(int i=1;i<=n;i++) scanf("%d", &b[i]);
for(int i=1;i<n;i++) scanf("%d", &c[i]);
for(int i=1;i<n;i++) {
int p = b[i], q = b[i+1], r = lca(p, q);
qry[p].push_back(query(c[i], dep[p]%c[i], 1, i));
qry[fa[0][r]].push_back(query(c[i], dep[p]%c[i], -1, i));
int k = (dep[r] + c[i] - (dep[p] - dep[r])%c[i])%c[i];
qry[q].push_back(query(c[i], k, 1, i));
qry[r].push_back(query(c[i], k, -1, i));
if( dep[q]%c[i] != k ) ans[i] += a[q];
}
dfs2(1);
for(int i=1;i<n;i++) printf("%d\n", ans[i]);
}

@details@

看到网上题解说树链剖分什么的,我只想说:

果然离线处理 + 差分好啊,一个 dfs 搞定的事情。

@bzoj - 4381@ [POI2015] Odwiedziny的更多相关文章

  1. [POI2015]Odwiedziny

    [POI2015]Odwiedziny 题目大意: 一棵\(n(n\le5\times10^4)\)个点的树,\(n\)次询问从一个点到另一个点的路径上,每次跳\(k\)个点,所经过的点权和. 思路: ...

  2. bzoj 4386: [POI2015]Wycieczki

    bzoj 4386: [POI2015]Wycieczki 这题什么素质,爆long long就算了,连int128都爆……最后还是用long double卡过的……而且可能是我本身自带大常数吧,T了 ...

  3. BZOJ 4385: [POI2015]Wilcze doły

    4385: [POI2015]Wilcze doły Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 648  Solved: 263[Submit][ ...

  4. BZOJ 4384: [POI2015]Trzy wieże

    4384: [POI2015]Trzy wieże Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 217  Solved: 61[Submit][St ...

  5. Bzoj 3747: [POI2015]Kinoman 线段树

    3747: [POI2015]Kinoman Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 553  Solved: 222[Submit][Stat ...

  6. BZOJ 3747 POI2015 Kinoman 段树

    标题效果:有m点,每个点都有一个权值.现在我们有这个m为点的长度n该序列,寻求区间,它仅出现一次在正确的点区间内值和最大 想了很久,甚至神标题,奔说是水的问题--我醉了 枚举左点 对于每个请求留点右键 ...

  7. BZOJ 4380 [POI2015]Myjnie | DP

    链接 BZOJ 4380 题面 有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]. 有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个 ...

  8. bzoj4381: [POI2015]Odwiedziny

    这题搞了我一下午……因为一些傻X的问题…… 对于步长大于sqrt(n)的询问,我们可以直接暴力求解 然后,我们可以事先预处理出d[u][step]表示u往上跳,每次跳step步,直到跳到不能跳为止,所 ...

  9. BZOJ 3747 POI2015 Kinoman

    因为上午没有准备够题目,结果发现写完这道题没题可写了QAQ 又因为这道题范围是100w,我写了发线段树,以为要T,上午就花了一个小时拼命卡常数 结果下午一交居然过了QAQ 我们考虑枚举L,求最大R使得 ...

随机推荐

  1. win7使用经验-调整cmd窗口大小

    分享一个调整cmd窗口的方法: 1.右击标题栏空白处,选择属性 2.选择布局栏 3.修改屏幕缓冲区大小的宽度和高度(自定义) 4.确定 注意:这里的缓冲区大小是指用户可拖动缩放的范围,并不是cmd窗口 ...

  2. 文本流向 layout-flow

    作用与语法 文本流向layout-flow用来定义网页中的文本流向方式. 即排列方式,主要有两个属性,分别是horizonta (水平的) 和vertical-ideographic (垂直的). 文 ...

  3. 实用Jupyter Notebook扩展工具——提升你的工作效率

    Jupyter Notebook 现已成为数据分析,机器学习的必备工具.因为它可以让数据分析师集中精力向用户解释整个分析过程.通过安装一些扩展工具,可以让你在Jupyter Notebook上的工作效 ...

  4. linux学习 网络系统&文件查找

    被用来配置和操作网络系统的命令数目非常巨大,这里仅仅介绍一些经常会使用的 这一章会介绍ssh远程登录程序哦 ping 命令(掠过) traceroute 会显示主机到指定的主机要经过的跳数的网络流量列 ...

  5. day37 08-Hibernate的反向工程

    反向工程:先创建表,创建好表之后,就是持久化类和映射文件可以不用你写,而且你的DAO它也可以帮你生成.但是它生成的DAO可能会多很多的方法.你可以不用那么多方法,但是它里面提供了这种的.用hibern ...

  6. MySQL Daemon failed to start错误解决办法是什么呢?

    首先我尝试用命令:service mysql start 来启动服务,但是提示: MySQL Daemon failed to start 一开始出现这个问题我很方,然后开始查,说什么的都有,然后看到 ...

  7. 微信小程序之项目的创建

    之前就想学习一下微信小程序,只不过前段时间在学习Java中的一些线程的知识,拖了很久也没有实行这个目标,今天终于从网上找了一个完整的视频,来从头学习完整的开发流程,现在逐渐有一种想法,就是自己构建项目 ...

  8. 模拟4题解 T1礼物

    T1 题目描述 夏川的生日就要到了.作为夏川形式上的男朋友,季堂打算给夏川买一些生 日礼物. 商店里一共有种礼物.夏川每得到一种礼物,就会获得相应喜悦值Wi(每种 礼物的喜悦值不能重复获得). 每次, ...

  9. ACdream 1007 (快速幂)

    题目链接 a + b Time Limit: 2000/1000MS (Java/Others)Memory Limit: 128000/64000KB (Java/Others) Problem D ...

  10. 数据挖掘案例:基于 ReliefF和K-means算法的应用

    数据挖掘案例:基于 ReliefF和K-means算法的应用 数据挖掘方法的提出,让人们有能力最终认识数据的真正价值,即蕴藏在数据中的信息和知识.数据挖掘(DataMiriing),指的是从大型数据库 ...