题目传送门

  传送点I

  传送点II

题目大意

  (此题目不需要大意,我认为它已经很简洁了)

  显然线段树合并(我也不知道哪来这么多显然)

  考虑将每条路径拆成两条路径 s -> lca 和 t -> lca 。

  对于前一种路径上的某一点i,希望在时刻 w[i] 经过它,那么就有

dep[s] - dep[i] = w[i]

  移项可得:

dep[s] = w[i] + dep[i]

  然后发现dep[s]可以被看做已知条件,那么根据常用套路,在点s将线段树dep[s]处的值 + 1,在lca处还原。

  回溯的过程中通过 w[i] + dep[i] 查答案就好了。

  对于后一种路径,考虑在点t的时候将线段树的某个位置 + 1,在lca计算答案之前还原。那么就有:

dep[s] + dep[i] - * dep[lca] = w[i]

  然后移项得到:

dep[s] - * dep[lca] = w[i] - dep[i]

  继续扔进线段树里维护和查询。

  (前年做这道题的时候完全没有思路,去年发现原来这么水。。)

Code

 /**
* luogu
* Problem#1600
* Accepted
* Time: 6321ms
* Memory: 172394k
*/
#include <bits/stdc++.h>
using namespace std;
#define smin(_a, _b) _a = min(_a, _b)
#define smax(_a, _b) _a = max(_a, _b)
#define fi first
#define sc second
typedef pair<int, int> pii;
typedef bool boolean;
template<typename T>
inline void readInteger(T& u) {
static char x;
while(!isdigit(x = getchar()));
for(u = x - ''; isdigit(x = getchar()); u = u * + x - '');
} typedef class SegTreeNode {
public:
int val;
SegTreeNode *l, *r; SegTreeNode(int val = , SegTreeNode* l = NULL, SegTreeNode* r = NULL):val(val), l(l), r(r) { } inline void pushUp() {
val = l->val + r->val;
}
}SegTreeNode; #define LIMIT 2000000
SegTreeNode pool[LIMIT + ];
SegTreeNode *top = pool;
SegTreeNode null(, &null, &null); SegTreeNode* newnode() {
if(top >= pool + LIMIT)
return new SegTreeNode(, &null, &null);
*top = SegTreeNode(, &null, &null);
return top++;
} #define null &null void merge(SegTreeNode*& a, SegTreeNode* b) {
if(a == null) {
a = b;
return;
}
if(b == null) return;
a->val += b->val;
merge(a->l, b->l);
merge(a->r, b->r);
} typedef class SegTree {
public:
SegTreeNode* root; SegTree():root(null) { } void update(SegTreeNode*& node, int l, int r, int idx, int val) {
if(node == null)
node = newnode();
if(l == idx && r == idx) {
node->val += val;
return;
}
int mid = (l + r) >> ;
if(idx <= mid)
update(node->l, l, mid, idx, val);
else
update(node->r, mid + , r, idx, val);
node->pushUp();
} int query(SegTreeNode*& node, int l, int r, int idx) {
if(node == null) return ;
if(l == idx && r == idx)
return node->val;
int mid = (l + r) >> ;
if(idx <= mid)
return query(node->l, l, mid, idx);
return query(node->r, mid + , r, idx);
}
}SegTree; typedef class Query {
public:
int s;
int t;
int lca; Query(int s = , int t = , int lca = ):s(s), t(t), lca(lca) { }
}Query; int n, m;
int* wss;
Query* qs;
vector<int> *q;
vector<int> *g; inline void init() {
readInteger(n);
readInteger(m);
wss = new int[(n + )];
qs = new Query[(m + )];
q = new vector<int>[(n + )];
g = new vector<int>[(n + )];
for(int i = , u, v; i < n; i++) {
readInteger(u);
readInteger(v);
g[u].push_back(v);
g[v].push_back(u);
}
for(int i = ; i <= n; i++)
readInteger(wss[i]);
for(int i = , s, t; i <= m; i++) {
readInteger(s);
readInteger(t);
qs[i] = Query(s, t, );
q[s].push_back(i);
if(s != t)
q[t].push_back(i);
}
} int* f;
int* dep;
int find(int x) {
return (f[x] == x) ? (x) : (f[x] = find(f[x]));
} void tarjan(int node, int fa) {
f[node] = node;
dep[node] = dep[fa] + ;
for(int i = ; i < (signed)g[node].size(); i++) {
int& e = g[node][i];
if(e == fa) continue;
tarjan(e, node);
f[e] = node;
}
for(int i = ; i < (signed)q[node].size(); i++) {
int id = q[node][i];
if(f[qs[id].s] && f[qs[id].t] && !qs[id].lca)
qs[id].lca = find(f[(qs[id].s == node) ? (qs[id].t) : (qs[id].s)]);
}
} int *ans;
vector<int>* ls;
SegTreeNode* dfs1(int node, int fa) {
SegTree st;
for(int i = ; i < (signed)q[node].size(); i++) {
Query &aq = qs[q[node][i]];
if(aq.s == node) {
st.update(st.root, , n, dep[aq.s], );
ls[aq.lca].push_back(q[node][i]);
}
}
for(int i = ; i < (signed)g[node].size(); i++) {
int& e = g[node][i];
if(e == fa) continue;
merge(st.root, dfs1(e, node));
}
if(dep[node] + wss[node] <= n)
ans[node] = st.query(st.root, , n, dep[node] + wss[node]);
else
ans[node] = ;
for(int i = ; i < (signed)ls[node].size(); i++)
st.update(st.root, , n, dep[qs[ls[node][i]].s], -);
ls[node].clear();
return st.root;
} SegTreeNode* dfs2(int node, int fa) {
SegTree st;
for(int i = ; i < (signed)q[node].size(); i++) {
Query &aq = qs[q[node][i]];
if(aq.t == node) {
st.update(st.root, -n, n, dep[aq.s] - * dep[aq.lca], );
ls[aq.lca].push_back(q[node][i]);
}
}
for(int i = ; i < (signed)g[node].size(); i++) {
int& e = g[node][i];
if(e == fa) continue;
merge(st.root, dfs2(e, node));
}
for(int i = ; i < (signed)ls[node].size(); i++)
st.update(st.root, -n, n, dep[qs[ls[node][i]].s] - * dep[qs[ls[node][i]].lca], -);
ans[node] += st.query(st.root, -n, n, wss[node] - dep[node]);
return st.root;
} inline void solve() {
f = new int[(n + )];
dep = new int[(n + )];
dep[] = ;
memset(f, , sizeof(int) * (n + ));
tarjan(, );
ans = new int[(n + )];
ls = new vector<int>[(n + )];
dfs1(, );
top = pool;
for(int i = ; i <= n; i++)
assert(ls[i].empty());
dfs2(, );
for(int i = ; i <= n; i++)
printf("%d ", ans[i]);
} int main() {
init();
solve();
return ;
}

Segment Tree

  线段树不优秀,跑得太慢了。ccf老年机应该会跑T。

  我们发现,这个本质上是在dfs序某些位置某一下标修改一个值,然后询问区间某一下标的和。

  这个完全可以用前缀和相减,而没必要出动线段树。

  比较简单的写法就是:访问到一个点,记录一下它询问的下标的值,然后再递归它的子树,最后加上差值。

Code

 /**
* uoj
* Problem#261
* Accepted
* Time: 1236ms
* Memory: 59108k
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
using namespace std;
typedef bool boolean; const int N = 3e5 + , N2 = N << ; #define pii pair<int, int>
#define fi first
#define sc second template <typename T>
void pfill(T* ps, const T* ped, T val) {
for ( ; ps != ped; *ps = val, ps++);
} template <typename T>
class MapManager {
public:
int* h;
vector< pair<T, int> > vs; MapManager() { }
MapManager(int n) {
h = new int[(n + )];
pfill(h, h + n + , -);
} void insert(int p, T dt) {
vs.push_back(make_pair(dt, h[p]));
h[p] = (signed) vs.size() - ;
} pair<T, int>& operator [] (int p) {
return vs[p];
} }; int n, m;
int *dep;
int *wts, *res;
int *lcas, *us, *vs;
boolean *exi;
MapManager<int> g;
MapManager<pii> qlca; // fi: another node. sc: id
MapManager<int> as, rs;
MapManager<pii> ms; // sc: val inline void init() {
scanf("%d%d", &n, &m);
wts = new int[(n + )];
res = new int[(n + )];
exi = new boolean[(m + )];
pfill(res + , res + n + , );
pfill(exi, exi + m + , true);
g = MapManager<int>(n);
for (int i = , u, v; i < n; i++) {
scanf("%d%d", &u, &v);
g.insert(u, v), g.insert(v, u);
}
for (int i = ; i <= n; i++)
scanf("%d", wts + i);
us = new int[(n + )];
vs = new int[(n + )];
lcas = new int[(n + )];
qlca = MapManager<pii>(n);
for (int i = , u, v; i <= m; i++) {
scanf("%d%d", &u, &v);
qlca.insert(u, pii(v, i));
qlca.insert(v, pii(u, i));
us[i] = u, vs[i] = v;
}
} int *uf;
boolean *vis;
int dfs_clock; int find(int x) {
return (uf[x] == x) ? (x) : (uf[x] = find(uf[x]));
} void tarjan(int p, int fa, int dp) {
vis[p] = true, dep[p] = dp;
for (int i = g.h[p], e; ~i; i = g[i].sc) {
if ((e = g[i].fi) == fa)
continue;
tarjan(e, p, dp + );
uf[find(e)] = p;
} for (int i = qlca.h[p]; ~i; i = qlca[i].sc) {
pii d = qlca[i].fi;
if (vis[d.fi] && exi[d.sc]) {
exi[d.sc] = false;
lcas[d.sc] = find(d.fi);
}
}
} int bucket[N2]; void put(int p, int val) {
(p < ) ? (p += N) : ();
bucket[p] += val;
} int get(int p) {
(p < ) ? (p += N) : ();
return bucket[p];
} void dfs1(int p, int fa) {
int tmp = get(wts[p] + dep[p]);
for (int i = as.h[p]; ~i; i = as[i].sc)
put(as[i].fi, );
for (int i = g.h[p], e; ~i; i = g[i].sc) {
if ((e = g[i].fi) == fa)
continue;
dfs1(e, p);
}
res[p] += get(wts[p] + dep[p]) - tmp;
for (int i = rs.h[p]; ~i; i = rs[i].sc)
put(rs[i].fi, -);
} void dfs2(int p, int fa) {
int tmp = get(wts[p] - dep[p]);
for (int i = ms.h[p]; ~i; i = ms[i].sc)
put(ms[i].fi.fi, ms[i].fi.sc);
for (int i = g.h[p], e; ~i; i = g[i].sc) {
if ((e = g[i].fi) == fa)
continue;
dfs2(e, p);
}
res[p] += get(wts[p] - dep[p]) - tmp;
} inline void solve() {
uf = new int[(n + )];
dep = new int[(n + )];
vis = new boolean[(n + )];
for (int i = ; i <= n; i++)
uf[i] = i;
pfill(vis + , vis + n + , false);
tarjan(, , ); delete[] vis;
delete[] exi; as = MapManager<int>(n);
rs = MapManager<int>(n);
ms = MapManager<pii>(n); for (int i = ; i <= m; i++) {
int u = us[i], v = vs[i], g = lcas[i];
as.insert(u, dep[u]);
rs.insert(g, dep[u]);
ms.insert(v, pii(dep[u] - * dep[g], ));
ms.insert(g, pii(dep[u] - * dep[g], -));
} dfs1(, );
dfs2(, );
for (int i = ; i <= n; i++)
printf("%d ", res[i]);
} int main() {
init();
solve();
return ;
}

NOIP 2016 天天爱跑步 (luogu 1600 & uoj 261) - 线段树的更多相关文章

  1. [NOIP]2016天天爱跑步

    [NOIP]2016天天爱跑步 标签: LCA 树上差分 NOIP Description 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...

  2. NOIP 2016 天天爱跑步 80分暴力

    题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点 ...

  3. [NOIp 2016]天天爱跑步

    Description 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图 ...

  4. Noip 2016 天天爱跑步 题解

    [NOIP2016]天天爱跑步 时间限制:2 s   内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...

  5. UOJ261 【NOIP2016】天天爱跑步 LCA+动态开点线段树

    UOJ261 [NOIP2016]天天爱跑步 Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.天天爱跑步是一个养成类游戏,需要玩家每天按时上线, ...

  6. 2016暑假多校联合---Rikka with Sequence (线段树)

    2016暑假多校联合---Rikka with Sequence (线段树) Problem Description As we know, Rikka is poor at math. Yuta i ...

  7. [Luogu P4215] 踩气球 (线段树)

    题面 传送门:https://www.luogu.org/problemnew/show/P4215 Solution 这题十分有意思. 首先,我们可以先想想离线做法,因为在线做法可以从离线做法推出. ...

  8. 【NOIP】提高组2016 天天爱跑步

    [题意]n个点的树,有m个人同时开始走链,每一步花一秒,n个点都有观察员在ai秒观察,求每个观察员观察到的人数. [算法]树上差分(主席树||线段树合并) [题解]一个人的走链可以拆成u-lca和lc ...

  9. NOIp 2012 #2 借教室 Label:区间修改线段树

    题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然 ...

随机推荐

  1. 图像中的stride含义

    这个不是卷积中的步长stride 是另外一个含义, stride = 每个像素所占字节数 * width input stride为我们正常进行卷积时候设置的stride值,output stride ...

  2. !! zcl_TD 用法注释02 力攻(动能<4)

    力攻(动能<4)创新高下M5可持有力攻(动能<4)不创新高下M5可减仓

  3. mysql----------阿里云RDS导入导出

    1.这是阿里云rds如何将导出的物理备份文件,导入到自建库里面: https://help.aliyun.com/knowledge_detail/5973700.html?spm=5176.7766 ...

  4. Linux SSH 免秘钥登录

    SSH 免秘钥登录 ssh:是一种安全加密协议 ssh  username@hostname     ssh gongziyuan.com:以当前用户登录该机器(如果不是当前用户,需要这么干:ssh ...

  5. PHP语句函数

    运算符 +.-.*./.++.--.+=.-=.% 字符串拼接用.      js里面用+拼接 逻辑运算符 and &&  .  or ||   . ! 错误运算符 @(可以抑制错误) ...

  6. IT新起之秀

    辞职以后自己比较迷茫,不知道自己能干什么,09年毕业到现在虽然工作经验有7.8年,但是感觉自己什么都不会,除了自己能下车间别的好像也做不成,没有一技之长.我更像是一个经验用了7.8年而不是有7.8年的 ...

  7. I/O流的概念和流类库的结构

    概念: 程序的输入指的是从输入文件将数据传送给程序,程序的输出指的是从程序将数据传送给输出文件. C++输入输出包含以下三个方面的内容: 1.对系统指定的标准设备的输入和输出.即从键盘输入数据,输出到 ...

  8. FILE文件删除操作(删除指定文件夹下所有文件和文件夹包括子文件夹下所有文件和文件夹),就是删除所有

    2018-11-05  19:42:08开始写 选择 删除 1.FileUtils.java类 import java.io.File;//导入包 import java.util.List;//导入 ...

  9. 使用Navicat导入SQL文件

    2018-11-04 19:35:12 开始写 刷新后就可以看见导入的数据库了(按F5刷新) 谢谢.Thank you.Salamat Do(撒拉玛特朵).あリがCám o*n(嘉蒙)とゥ(阿里嘎都) ...

  10. Spring boot FastJson

    介绍:FastJson 是ailibaba 的一款解析Json的开源框架 使用方式1 引入jar 包 <dependency>    <groupId>com.alibaba& ...