当初随便出的一道 思博题 竟然被交换到了八中 QAQ

然后就上了 BZOJ 。。。作为原作者还是把原来写的详细题解放出来吧 qwq

题意

\(n\) 个点的数,每个点初始有权值 \(v_i\) ,需要支持 \(m\) 次操作。

动态单点加权值,查询子树内点对的距离和。

题解

5pts

观察到第 \(9\) 个点,只有修改,没有询问。只需要正确打开文件并且不输出任何字符即可。

(注意暴力不能 \(RE\) 或者 \(MLE\) 与 \(TLE\) 才能 艰难地 拿到这个分)

15pts

对于 \(n, m \le 50\) 的点,每次直接修改点权。

询问的时候暴力枚举子树中的点对,然后跳 \(Lca\) 计算距离。

复杂度是 \(O(mn^3)\) 的。

30pts

发现暴力枚举点对的时候不需要重新跳 \(Lca\) ,直接从每个点 \(Bfs\) or \(Dfs\) 即可。

复杂度是 \(O(mn^2)\) 的。

45pts

考虑把暴力这颗子树弄出来,就变成求树上两两点对带权距离和的经典问题。

如果工业一点就可以写个点分治,复杂度就是 \(O(mn \log n)\) 的,常数优秀应该可以得到这些分。

但其实没有这么麻烦,如果是边权的话,每个边被多少条路径经过,其实就是两边子树 \(size\) (子树大小)的乘积,也就是 \(size * (tot - size + 1)\) 。

但这题权值是在点上的,所以要枚举它每一个儿子顺次计算,就是每个点的 \(size\) 和前面的 \(size\) 之和的乘积,最后加上子树内外的 \(size\) 之积就行了。然后复杂度就是 \(O(nm)\) 的。

还有一种一遍 \(dp\) 一遍换根的做法就不多赘述了,复杂度也是 \(O(nm)\) 的。

55pts

其实刚刚那个 \(O(nm \log n)\) 或者 \(O(nm)\) 的算法也可以通过 \(10, 11\) 号点的...

60pts

但对于只有询问的点,应该是有更好的做法,利用两点距离 \(dis_{a, b} = dep_a + dep_b - dep_{lca(a, b)} \times 2 + v_{lca(a, b)}\) 这个经典计算方式。

考虑每个点的 \(dep\) 计算了多少次,以及它作为 \(lca\) 时计算了多少次 \(dep\) 与 \(v\) 。

我们推导式子:

\[\begin{aligned}
ans &= \sum_{u \in child_p} \sum_{v \in child_p, v \ge u} dep_u + dep_v - dep_{lca(a, b)} * 2 + v_{lca(a, b)} \\
&= (\sum_{u \in child_p} dep_u) \times (size_p + 1) + \sum_{u \in child_p} (v_u - dep_u * 2) * coef_u
\end{aligned}
\]

此处 \(coef_u\) 为 \(u\) 作为 \(lca\) 出现的次数。至于求这个,可以依次考虑它的每个儿子,系数就是每个儿子的 \(sz\) 乘上前面所有 \(sz\) 的和(一开始要算上 \(u\) 点)。

我们用 \(Dfs\) 预处理就行了,记下当前的 \(\displaystyle \sum_{u \in child_p} dep_u\) 的值,以及 \(\displaystyle \sum_{u \in child_p} (v_u - dep_u * 2) * coef_u\) 即可在 \(O(n)\) 的时间里预处理出所有点的答案。

80pts

\(u_i = v_i - 1\) :直接考虑每个点被多少个区间覆盖即可,用线段树支持动态修改和查询。

\(u_i = 1\) :分类讨论即可。询问的时候 \(p = 2\) ,\(u \not = 1\) 的时候直接输出 \(v_p\) ,\(u = 1\) 的时候也可以十分轻易地直接维护答案。

这些点只是为了凑部分分的。

100pts

方法一

至于正解,我们考虑动态维护前面 \(60pts\) 的式子。

我们发现每次只需要动态查询子树的 \(\sum dep_u\) (带权深度)的和,以及 \(\displaystyle \sum_{u \in child_p} (v_u - dep_u * 2) * coef_u\) 就行了。

每次修改单点权值,等价于修改子树内所有点的带权深度和,我们用线段树支持子树修改就行了,然后询问的时候就子树询问。

至于 \(*~coef_u\) ,我们对于线段树每个区间维护 \(coef\) 的和,每次给线段树区间加的时候,把 \(sum\) 加上 \(coef \times x\) 就行了,复杂度就是 \(O(m \log n)\) 的。

方法二

考虑前面 \(45pts\) 其中的一种做法,考虑一个点被多少条路径穿过。

我们先假设只询问全树,那么可以用树状数组维护每个点的系数。那么就可以直接单点修改,区间查询就行了。

如果是询问子树的话,也是很简单的,我们减去经过子树 \(u\) 内点的路径的多余贡献就行了。具体来说,就是子树 \(u\) 中每个点的 \(size\) 乘上子树 \(u\) 外的点数 \(n - size_u\) 就行了。

同样这个也可以用树状数组实现,十分的好写好调。

一些有意思的东西

这题应该是一道原创 送分 题,其实思路十分的简单,体现了出题人的良心。

考察了对于代码的实现以及对于数据结构的实现。

就是代码实现略微有点细节,利用了一个差分的小 \(trick\) 。

在出完这道题后,找 Hometown 验题的时候,他告诉我了方法二,简单好写常数小,发现 \(std\) 又双叒叕被踩了。。。 果然我只能出思博题啊!

代码

方法一

这是出题人一开始想到的一个 sb 方法,常数大,还难写。。

/**************************************************************
Problem: 5477
User: zjp_shadow
Language: C++
Result: Accepted
Time:8816 ms
Memory:67256 kb
****************************************************************/ #include <bits/stdc++.h> #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define pb push_back using namespace std; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} typedef long long ll; const int N = 3e5 + 1e3, Mod = 1e9 + 7; vector<int> G[N]; int id[N]; inline int Plus(int a, int b) {
return (a += b) >= Mod ? a - Mod : a;
} #define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r template<int Maxn>
struct Segment_Tree { int coef[Maxn], sumv[Maxn], tag[Maxn]; inline void Modify(int o, int uv) {
tag[o] = Plus(tag[o], uv);
sumv[o] = (sumv[o] + 1ll * coef[o] * uv) % Mod;
} inline void Push_Down(int o) {
if (tag[o])
Modify(o << 1, tag[o]), Modify(o << 1 | 1, tag[o]), tag[o] = 0;
} inline void Push_Up(int o) {
sumv[o] = Plus(sumv[o << 1], sumv[o << 1 | 1]);
} void Build(int o, int l, int r, int *base, int *cur) {
if (l == r) { sumv[o] = 1ll * (coef[o] = base[id[l]]) * cur[id[l]] % Mod; return ; }
int mid = (l + r) >> 1;
Build(lson, base, cur); Build(rson, base, cur);
Push_Up(o); coef[o] = Plus(coef[o << 1], coef[o << 1 | 1]);
} void Update(int o, int l, int r, int ul, int ur, int uv) {
if (ul <= l && r <= ur) { Modify(o, uv); return ; }
int mid = (l + r) >> 1; Push_Down(o);
if (ul <= mid) Update(lson, ul, ur, uv);
if (ur > mid) Update(rson, ul, ur, uv); Push_Up(o);
} int Query(int o, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return sumv[o];
int mid = (l + r) >> 1, res = 0; Push_Down(o);
if (ql <= mid) res = Plus(res, Query(lson, ql, qr));
if (qr > mid) res = Plus(res, Query(rson, ql, qr));
Push_Up(o); return res;
} }; #undef lson
#undef rson Segment_Tree<N << 2> T1, T2, T3; int coef[N], dfn[N], efn[N], sz[N], dep[N]; void Dfs_Init(int u = 1, int fa = 0) {
static int clk = 0;
dep[u] = dep[fa] + 1;
id[dfn[u] = ++ clk] = u; coef[u] = sz[u] = 1;
Rep (i, G[u].size()) {
int v = G[u][i];
if (v != fa) {
Dfs_Init(v, u);
coef[u] = (coef[u] + 1ll * sz[u] * sz[v]) % Mod;
sz[u] += sz[v];
}
}
efn[u] = clk;
} int main () { //freopen ("transport.in", "r", stdin);
//freopen ("transport.out", "w", stdout); int n = read(), m = read(); For (i, 1, n - 1) {
int u = read(), v = read();
G[u].pb(v); G[v].pb(u);
} Dfs_Init(); int I[N]; For (i, 1, n) I[i] = 1; T1.Build(1, 1, n, I, dep);
T2.Build(1, 1, n, coef, dep);
T3.Build(1, 1, n, coef, I); For (i, 1, m) {
int opt = read(), pos = read();
if (opt == 1) {
int val = read();
T1.Update(1, 1, n, dfn[pos], efn[pos], val);
T2.Update(1, 1, n, dfn[pos], efn[pos], val);
T3.Update(1, 1, n, dfn[pos], dfn[pos], val);
} else { long long ans =
1ll * T1.Query(1, 1, n, dfn[pos], efn[pos]) * (sz[pos] + 1)
- T2.Query(1, 1, n, dfn[pos], efn[pos]) * 2ll
+ T3.Query(1, 1, n, dfn[pos], efn[pos]); printf ("%lld\n", (ans % Mod + Mod) % Mod);
}
} return 0;
}

方法二

简单的树状数组解法 QAQ

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define pb push_back using namespace std; typedef long long ll; template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; } inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} const int N = 3e5 + 1e3, Mod = 1e9 + 7; vector<int> G[N]; int n, dfn[N], efn[N], sz[N], coef[N]; inline int Plus(int a, int b) {
return (a += b) >= Mod ? a - Mod : a;
} #define lowbit(x) (x & -x)
template<int Maxn>
struct Fenwick_Tree { int sumv[Maxn]; inline void Update(int pos, int uv) {
for (; pos <= n; pos += lowbit(pos))
sumv[pos] = Plus(sumv[pos], uv);
} inline int Query(int pos) {
int res = 0;
for (; pos; pos -= lowbit(pos))
res = Plus(res, sumv[pos]);
return res;
} inline int Query(int l, int r) {
return Query(r) - Query(l - 1);
} }; Fenwick_Tree<N> T1, T2; void Dfs_Init(int u = 1, int fa = 0) {
static int clk = 0;
dfn[u] = ++ clk; sz[u] = coef[u] = 1;
Rep (i, G[u].size()) {
int v = G[u][i];
if (v != fa) {
Dfs_Init(v, u);
coef[u] = (coef[u] + 1ll * sz[u] * sz[v]) % Mod;
sz[u] += sz[v];
}
}
coef[u] = (coef[u] + 1ll * sz[u] * (n - sz[u])) % Mod; T1.Update(dfn[u], coef[u]);
T2.Update(dfn[u], sz[u]); efn[u] = clk;
} int main () { ////freopen ("transport.in", "r", stdin);
////freopen ("transport.out", "w", stdout); n = read(); int m = read(); For (i, 1, n - 1) {
int u = read(), v = read();
G[u].pb(v); G[v].pb(u);
} Dfs_Init(); For (i, 1, m) {
int opt = read(), pos = read();
if (opt == 1) {
int val = read();
T1.Update(dfn[pos], 1ll * val * coef[pos] % Mod);
T2.Update(dfn[pos], 1ll * val * sz[pos] % Mod);
} else {
long long ans =
T1.Query(dfn[pos], efn[pos])
- 1ll * T2.Query(dfn[pos], efn[pos]) * (n - sz[pos]);
printf ("%lld\n", (ans % Mod + Mod) % Mod);
}
} return 0; }

BZOJ 5477: 星际穿越的更多相关文章

  1. [PKUSC2018]星际穿越

    [PKUSC2018]星际穿越 题目大意: 有一排编号为\(1\sim n\)的\(n(n\le3\times10^5)\)个点,第\(i(i\ge 2)\)个点与\([l_i,i-1]\)之间所有点 ...

  2. BZOJ5371[Pkusc2018]星际穿越——可持久化线段树+DP

    题目描述 有n个星球,它们的编号是1到n,它们坐落在同一个星系内,这个星系可以抽象为一条数轴,每个星球都是数轴上的一个点, 特别地,编号为i的星球的坐标是i. 一开始,由于科技上的原因,这n个星球的居 ...

  3. LOJ #6435. 「PKUSC2018」星际穿越(倍增)

    题面 LOJ#6435. 「PKUSC2018」星际穿越 题解 参考了 这位大佬的博客 这道题好恶心啊qwq~~ 首先一定要认真阅读题目 !! 注意 \(l_i<r_i<x_i\) 这个条 ...

  4. (纪录片)《星际穿越》中的科学 The Science of Interstellar

    简介: 导演: Gail Willumsen编剧: Gail Willumsen主演: 克里斯托弗·诺兰 / 乔纳森·诺兰 / 基普·索恩 / 马修·麦康纳类型: 纪录片 / 短片制片国家/地区: 美 ...

  5. 「PKUSC2018」星际穿越 (70分做法)

    5371: [Pkusc2018]星际穿越 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 27  Solved: 11[Submit][Status] ...

  6. [LOJ 6435][PKUSC 2018]星际穿越

    [LOJ 6435][PKUSC 2018]星际穿越 题意 给定 \(n\) 个点, 每个点与 \([l_i,i-1]\) 之间的点建立有单位距离的双向边. \(q\) 组询问从 \(x\) 走到 \ ...

  7. [Luogu 5465] [LOJ 6435] [PKUSC2018]星际穿越(倍增)

    [Luogu 5465] [LOJ 6435] [PKUSC2018]星际穿越(倍增) 题面 n个点的图,点i和[l[i],i)的所有点连双向边.每次询问(l,r,x)表示x到[l,r]的所有点的最短 ...

  8. LOJ.6435.[PKUSC2018]星际穿越(倍增)

    LOJ BZOJ 参考这儿qwq. 首先询问都是求,向左走的最短路. \(f[i][j]\)表示从\(i\)走到\(j\)最少需要多少步.表示这样只会\(O(n^2\log n)\)的= =但是感觉能 ...

  9. 2019.03.09 bzoj5371: [Pkusc2018]星际穿越(主席树)

    传送门 题意简述: 给一个序列,对于第iii个位置,它跟[limi,i−1][lim_i,i-1][limi​,i−1]这些位置都存在一条长度为111的无向边. 称dist(u,v)dist(u,v) ...

随机推荐

  1. Continued Fractions CodeForces - 305B (java+高精 / 数学)

    A continued fraction of height n is a fraction of form . You are given two rational numbers, one is ...

  2. numpy中random的使用

    import numpy as np a=np.random.random()#用于生成一个0到1的随机浮点数: 0 <= n < 1.0print(a)0.772000903322952 ...

  3. 快速失败/报错机制 - fail-fast

    一.快速报错机制(fail-fast) 这是<Java编程思想>中关于快速报错机制的描述 Java容器有一种保护机制,能够防止多个进程同时修改同一个容器的内容.如果在你迭代遍历容器的过程中 ...

  4. php获取URL扩展名

    一切拿代码来说话: 举例:'http://www.sina.com.cn/abc/de/fg.php?id=1': $url = 'http://www.sina.com.cn/abc/de/fg.p ...

  5. React-Native windows环境搭建记录

    1.安装jdk,SDK Jdk下载地址:http://www.oracle.com/technetwork/cn/java/javase/downloads/jdk8-downloads-213315 ...

  6. java注解和自定义注解的简单使用

    前言 在使用Spring Boot的时候,大量使用注解的语法去替代XML配置文件,十分好用. 然而,在使用注解的时候只知道使用,却不知道原理.直到需要用到自定义注解的时候,才发现对注解原理一无所知,所 ...

  7. [转帖]CentOS 查看系统信息汇总

    CentOS 查看系统信息汇总 http://blog.itpub.net/15498/viewspace-2637493/ 感觉应该是 centos相关的 改了下名字 日志文件说明 /var/log ...

  8. 123. 单词搜索(DFS)

    描述 给出一个二维的字母板和一个单词,寻找字母板网格中是否存在这个单词. 单词可以由按顺序的相邻单元的字母组成,其中相邻单元指的是水平或者垂直方向相邻.每个单元中的字母最多只能使用一次. 样例 给出 ...

  9. spring bean之间的关系:继承,依赖,注入

    一 继承 spring中多个bean之间的继承关系,和面向对象中的继承关系类似,直接看代码. 先定义一个Person类 package com.demo.spring.entity; /** * @a ...

  10. (自用 )npm --save和--save-dev区别

    https://blog.csdn.net/playboyanta123/article/details/78349034(原文) 目前大多数基于vue的项目都是用vue-cli 创建的.当创建项目完 ...