当初随便出的一道 思博题 竟然被交换到了八中 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. 在k-means或kNN,我们是用欧氏距离来计算最近的邻居之间的距离。为什么不用曼哈顿距离?

    曼哈顿距离只计算水平或垂直距离,有维度的限制.另一方面,欧氏距离可用于任何空间的距离计算问题. 因为,数据点可以存在于任何空间,欧氏距离是更可行的选择.例如:想象一下国际象棋棋盘,象或车所 做的移动是 ...

  2. p151开映射札记

    1. 如何理解这句话? 2.连续有什么用? 3.为什么区间包含,经过算子T还是包含? 谢谢 谢谢学长 我懂了  1.2.     3有点儿模糊 1.连续等价于开集原像是开集,而可逆算子的逆的原像就是的 ...

  3. vue实现双向数据绑定之Object.defineProperty()篇

    前言 vue.js中使用ES5的Object.defineProperty()实现数据的双向绑定 Object.defineProperty()原理 Object.defineProperty()可以 ...

  4. Java中List集合去除重复数据的四种方法

    1. 循环list中的所有元素然后删除重复   public static List removeDuplicate(List list) { for ( int i = 0 ; i < lis ...

  5. class面向对象-1

    一.基本定义 class cl(object): def __init(self,var) self.var=var def func(self,i) print('%s is in %s'%(i,s ...

  6. Java内存泄漏分析

    https://www.javatang.com/archives/2017/11/08/11582145.html?tdsourcetag=s_pcqq_aiomsg

  7. Django--CRM--QueryDict, 模糊搜索, 加行级锁

    一 . QueryDict的修改 # QueryDict正常是不允许修改的,要想往里面添加内容,需要另mutable=True dic = request.GET print(dic) # <Q ...

  8. Linux基础学习(12)--Linux服务管理

    第十二章——Linux服务管理 一.服务简介与分类 1.服务的分类: 注:独立的服务放在内存中(好处:响应的速率快,坏处:独立的服务越多,耗费的内存资源越多):xinetd服务本身是独立的,在内存中, ...

  9. StatefulSet

    StatefulSet: 1.稳点且唯一的网络标识符 2.稳点且持久的存储 3.有序.平滑的部署和扩展 4.有序.平滑的删除和终止 5.有序的滚动更新 三个组件组成:headless(无头服务)    ...

  10. ArcGIS中使用异步回调函数查询图层Graphic

    在我们的地图的操作中经常会有一些操作是需要通过画多边形或者画线来查找某一块区域内的特定的Graphics比如我们在做的交警的项目中通过框选来查找某一块区域中的摄像机,某一块区域中的警力.警情.警员等相 ...