题目传送门

  传送点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. cocos2d JS 中的数组拼接与排序

    var arrA = [];//创建三个局部变量的新数组 var arrB = []; var arrC = []; var newCards = this.MyMahjong;//创建一个新的局部变 ...

  2. Selenium基础知识(四)表单切换

    在测试过程中,经常会碰到frame和iframe,嵌套等情况 这种情况下直接通过id,name等等是无法定位到的 好在selenium替我们想到了这个问题switch_to方法解决问题 switch_ ...

  3. C#使用HttpWebRequest与HttpWebResponse模拟用户登录

    模拟艺龙旅游网登录 想模拟登录,首先整理一下流程 1.通过360浏览器(IE,火狐等等)F12开发人员工具抓到相关数据 2.获取验证码(拿到cookie),登录时也需要使用 3.登录 -------- ...

  4. ecshop 前台分页

    在当前需要分页的if最后div里面加入这句, <!-- #BeginLibraryItem "/library/pages.lbi" --><!-- #EndLi ...

  5. python绝对路径的表述方式 及 字符串的转义

    当我们打开某文件的路径时,应该时刻注意绝对路径的表示方法,例如打开某个txt文件时 1, with open(‘d:\77\111.txt’) as  f: f.read() 此时会报错  ,路径被反 ...

  6. html5-css综合练习

    div{    width: 600px;    height: 800px;    padding: 40px;    font-size: 12px;    line-height: 25px;  ...

  7. Codeforces Round #323

    div1 C 这题的是给了一个无限循环的子数组,问有多少个 (l,s)l代表起点s代表长度的循环串,使得所有的在原串中的每位都小于等于另外这个串(l<=n,1<=s<n) 像这样,我 ...

  8. linux 安装python3 date更新

    http://linux.51yip.com/ ntpdate -u ntp.aliyun.com   更新时间 centos 默认是有 python的,是2.7.5的 重启网络的命令  -- sys ...

  9. Django 自定义

    from django.db import models class MyCharfield(models.Field): def __init__(self,max_length,*args,**k ...

  10. GUI颜色、字体设置对话框

    %颜色设置对话框 uisetcolor %c 红色 c=uisetcolor %默认规定颜色 c=uisetcolor([ ]); %设置曲线颜色 h = plot([:]); c = uisetco ...