NOIP 2016 天天爱跑步 (luogu 1600 & uoj 261) - 线段树
显然线段树合并(我也不知道哪来这么多显然)
考虑将每条路径拆成两条路径 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) - 线段树的更多相关文章
- [NOIP]2016天天爱跑步
[NOIP]2016天天爱跑步 标签: LCA 树上差分 NOIP Description 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...
- NOIP 2016 天天爱跑步 80分暴力
题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.«天天爱跑步»是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图可以看作一一棵包含 个结点 ...
- [NOIp 2016]天天爱跑步
Description 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏的地图 ...
- Noip 2016 天天爱跑步 题解
[NOIP2016]天天爱跑步 时间限制:2 s 内存限制:512 MB [题目描述] 小C同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...
- UOJ261 【NOIP2016】天天爱跑步 LCA+动态开点线段树
UOJ261 [NOIP2016]天天爱跑步 Description 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.天天爱跑步是一个养成类游戏,需要玩家每天按时上线, ...
- 2016暑假多校联合---Rikka with Sequence (线段树)
2016暑假多校联合---Rikka with Sequence (线段树) Problem Description As we know, Rikka is poor at math. Yuta i ...
- [Luogu P4215] 踩气球 (线段树)
题面 传送门:https://www.luogu.org/problemnew/show/P4215 Solution 这题十分有意思. 首先,我们可以先想想离线做法,因为在线做法可以从离线做法推出. ...
- 【NOIP】提高组2016 天天爱跑步
[题意]n个点的树,有m个人同时开始走链,每一步花一秒,n个点都有观察员在ai秒观察,求每个观察员观察到的人数. [算法]树上差分(主席树||线段树合并) [题解]一个人的走链可以拆成u-lca和lc ...
- NOIp 2012 #2 借教室 Label:区间修改线段树
题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然 ...
随机推荐
- Cocos Creator 音频API控制调频
*****音频的一些控制***** cc.audioEngine.playMusic(this.BGAudio,true);//播放音乐(true代表循环) cc.audioEngine.stopMu ...
- c++基础:之封装
原创: 零灵柒 C/C++的编程教室 2月4日 什么是类 C++是什么?C++设计之初就是class with c,所以简单点说,C++就是带类的C,那么什么是类? 类,简单点说就是类型,在 ...
- 30.get和post的区别
POST和GET的区别 Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE.URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个 ...
- vue中使用echarts
1.下载依赖 cnpm i echarts -S 2.模块中引入 <template> <div class="analyzeSystem"> <di ...
- Hibernate框架的第四天
## Hibernate框架的第四天 ## ---------- **回顾:Hibernate框架的第三天** 1. 一对多关联关系映射 * JavaBean的编写 * 编写映射的配置文件 * 使用级 ...
- aop编程之前置通知
aop( Aspect-Oriented Programming)前置通知原理案例讲解 编程步骤: 定义接口 编写对象(被代理的对象即目标对象) 编写通知(前置通知即目标方法调用前调用) 在beans ...
- django-pagination配置出错处理
是否有人出现这类错误: 首先确认几个修改处: setting.py添加 INSTALLED_APPS = ( # ... 'pagination', ) 添加中间件 MIDDLEWARE_CLASSE ...
- html5-新布局元素header,footer
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- hdu5032 树状数组
题意: 对于一个1000*1000的Mushroom, 起点在(1,1)给定一个斜率和一个x,求由斜率和x所对应的直线构成的三角形内蘑菇的总值. 每个点的对应的值为(x+A)(y+B) 解 每个点都有 ...
- Python 1.安装
Python是一种开源语言,有很多第三方库. 1. Python3 及相关组件下载及安装 a. Python3下载:https://www.python.org/downloads/->点击以下 ...