P3398 仓鼠找sugar

题目描述

小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友?

小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧!

输入输出格式

输入格式:

第一行两个正整数n和q,表示这棵树节点的个数和询问的个数。

接下来n-1行,每行两个正整数u和v,表示节点u到节点v之间有一条边。

接下来q行,每行四个正整数a、b、c和d,表示节点编号,也就是一次询问,其意义如上。

输出格式:

对于每个询问,如果有公共点,输出大写字母“Y”;否则输出“N”。

输入输出样例

输入样例#1: 复制

5 5
2 5
4 2
1 3
1 4
5 1 5 1
2 2 1 4
4 1 3 4
3 1 1 5
3 5 1 4
输出样例#1: 复制

Y
N
Y
Y
Y

说明

__本题时限1s,内存限制128M,因新评测机速度较为接近NOIP评测机速度,请注意常数问题带来的影响。__

20%的数据 n<=200,q<=200

40%的数据 n<=2000,q<=2000

70%的数据 n<=50000,q<=50000

100%的数据 n<=100000,q<=100000

为什么要专门把这道题拿出来写?因为这道题有多种方法,链剖和倍增可以刚好一个对应一个方法!

就拿来写了。

这道题实际上就是判断树上两条路径是否有交点。第一次想到的是将其中一条链染色,在另外一条链上查询有没有被染色,实际上就是链剖+线段树区间修改/查询了。这个既好想又好写,一次a。

#include<iostream>
#include<cstdio>
using namespace std; int n, q; struct Node {
int v, nex;
Node ( int v = , int nex = ) :
v ( v ), nex ( nex ) { }
} Edge[]; int h[], stot;
void add ( int u, int v ) {
Edge[++stot] = Node ( v, h[u] );
h[u] = stot;
} int fa[], siz[], son[], dep[];
void dfs1 ( int u, int f ) {
fa[u] = f; siz[u] = ; dep[u] = dep[f] + ;
for ( int i = h[u]; i; i = Edge[i].nex ) {
int v = Edge[i].v;
if ( v == f ) continue;
dfs1 ( v, u );
siz[u] += siz[v];
if ( siz[v] > siz[son[u]] ) son[u] = v;
}
} int top[], in[], ti;
void dfs2 ( int u, int t ) {
top[u] = t; in[u] = ++ ti;
if ( son[u] ) dfs2 ( son[u], t );
for ( int i = h[u]; i; i = Edge[i].nex ) {
int v = Edge[i].v;
if ( v == fa[u] || v == son[u] ) continue;
dfs2 ( v, v );
}
} int TR[], tag[];
void update ( int nd ) { TR[nd] = TR[nd << ] + TR[nd << | ]; } void push_down ( int nd, int l, int r ) {
if ( tag[nd] ) {
int mid = ( l + r ) >> ;
tag[nd << ] += tag[nd];
tag[nd << | ] += tag[nd];
TR[nd << ] += tag[nd] * ( mid - l + );
TR[nd << | ] += tag[nd] * ( r - mid );
tag[nd] = ;
}
} void add ( int nd, int l, int r, int L, int R, int d ) {
if ( l >= L && r <= R ) {
TR[nd] += d * ( r - l + );
tag[nd] += d;
return ;
}
push_down ( nd, l, r );
int mid = ( l + r ) >> ;
if ( L <= mid ) add ( nd << , l, mid, L, R, d );
if ( R > mid ) add ( nd << | , mid + , r, L, R, d );
update ( nd );
} void add ( int u, int v, int d ) {
while ( top[u] != top[v] ) {
if ( dep[top[u]] < dep[top[v]] ) swap ( u, v );
add ( , , n, in[top[u]], in[u], d );
u = fa[top[u]];
}
if ( dep[u] < dep[v] ) swap ( u, v );
add ( , , n, in[v], in[u], d );
} int query ( int nd, int l, int r, int L, int R ) {
if ( l >= L && r <= R ) return TR[nd];
push_down ( nd, l, r );
int ans = ; int mid = ( l + r ) >> ;
if ( L <= mid ) ans += query ( nd << , l, mid, L, R );
if ( R > mid ) ans += query ( nd << | , mid + , r, L, R );
return ans;
} int query ( int u, int v ) {
int ans = ;
while ( top[u] != top[v] ) {
if ( dep[top[u]] < dep[top[v]] ) swap ( u, v );
ans += query ( , , n, in[top[u]], in[u] );
u = fa[top[u]];
}
if ( dep[u] < dep[v] ) swap ( u, v );
ans += query ( , , n, in[v], in[u] );
return ans;
} int main ( ) {
scanf ( "%d%d", &n, &q );
for ( int i = ; i < n; i ++ ) {
int u, v;
scanf ( "%d%d", &u, &v );
add ( u, v );
add ( v, u );
}
dfs1 ( , );
dfs2 ( , );
for ( int i = ; i <= q; i ++ ) {
int a, b, c, d;
scanf ( "%d%d%d%d", &a, &b, &c, &d );
add ( a, b, );
int tmp = query ( c, d );
if ( tmp ) printf ( "Y\n" );
else printf ( "N\n" );
add ( a, b, - );
}
return ;
}

然而这道题不需要数据结构维护也可以做到,只需要求$LCA$就可以。

我们可以发现两条链相交的性质:一条链的两端点的$LCA$必定在另一条链上(然而并不会证明),所以需要判断的就是一个点是否在一条链上。

而判断成立的条件有:

1、$dep[x]>=dep[LCA(s,t)]$

2、$LCA(x,s)==x||LCA(x,t)==x$

所以每次取出深度更深的$LCA$和另一条路径的两个端点来判断即可。

#include<iostream>
#include<cstdio>
using namespace std; const int P = ; int n, q; struct Node {
int v, nex;
Node ( int v = , int nex = ) :
v ( v ), nex ( nex ) { }
} Edge[]; int h[], stot;
void add ( int u, int v ) {
Edge[++stot] = Node ( v, h[u] );
h[u] = stot;
} int jum[][P+], dep[];
void dfs ( int u, int f ) {
jum[u][] = f;
for ( int i = ; i <= P; i ++ )
jum[u][i] = jum[jum[u][i-]][i-];
for ( int i = h[u]; i; i = Edge[i].nex ) {
int v = Edge[i].v;
if ( v == f ) continue;
dep[v] = dep[u] + ;
dfs ( v, u );
}
} int LCA ( int u, int v ) {
if ( dep[u] < dep[v] ) swap ( u, v );
int t = dep[u] - dep[v];
for ( int p = ; t; t >>= , p ++ )
if ( t & ) u = jum[u][p];
if ( u == v ) return u; for ( int p = P; p >= ; p -- )
if ( jum[u][p] != jum[v][p] ) u = jum[u][p], v = jum[v][p]; return jum[u][];
} int main ( ) {
scanf ( "%d%d", &n, &q );
for ( int i = ; i < n; i ++ ) {
int u, v;
scanf ( "%d%d", &u, &v );
add ( u, v ); add ( v, u );
}
dfs ( , );
for ( int i = ; i <= q; i ++ ) {
int a, b, c, d;
scanf ( "%d%d%d%d", &a, &b, &c, &d );
int S = LCA ( a, b ), T = LCA ( c, d );
if ( dep[S] < dep[T] ) {
swap ( S, T );
swap ( a, c );
swap ( b, d );
}
if ( LCA ( S, c ) == S || LCA ( S, d ) == S ) cout << "Y" << endl;
else cout << "N" << endl;
}
return ;
}

【树链剖分/倍增模板】【洛谷】3398:仓鼠找sugar的更多相关文章

  1. 洛谷 3398 仓鼠找sugar——树链剖分

    题目:https://www.luogu.org/problemnew/show/P3398 原来只要把值记录成第几次就行了. 别忘了while(top[a]!=top[b])之后还要走一步. #in ...

  2. 洛谷 3398 仓鼠找sugar 【模板】判断树上两链有交

    [题解] 题意就是判断树上两条链是否有交.口诀是“判有交,此链有彼祖”.即其中一条链的端点的Lca在另一条链上. 我们设两条链的端点的Lca中深度较大的为L2,对L2与另一条链的两个端点分别求Lca, ...

  3. 洛谷P3412 仓鼠找$Sugar\ II$题解(期望+统计论?)

    洛谷P3412 仓鼠找\(Sugar\ II\)题解(期望+统计论?) 标签:题解 阅读体验:https://zybuluo.com/Junlier/note/1327573 原题链接:洛谷P3412 ...

  4. 洛谷 P3398 仓鼠找sugar —— 树链剖分

    题目:https://www.luogu.org/problemnew/show/P3398 树链剖分一下,路径就变成线段树上的几个区间: 两条路径相交就是线段树上有区间相交,所以在相应位置打个标记, ...

  5. 洛谷 P3398 仓鼠找sugar 解题报告

    P3398 仓鼠找sugar 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而 ...

  6. 洛谷P3398 仓鼠找sugar [LCA]

    题目传送门 仓鼠找sugar 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而 ...

  7. 洛谷——P3398 仓鼠找sugar

    https://www.luogu.org/problem/show?pid=3398#sub 题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴 ...

  8. 洛谷P3398 仓鼠找sugar

    题目描述 小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n.地下洞穴是一个树形结构.这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c) ...

  9. 洛谷 [P3398] 仓鼠找sugar

    树剖求LCA 我们可以发现,两条路径ab,cd相交,当且仅当 dep[lca(a,b)]>=dep[lca(c,d)]&(lca(lca(a,b),c)==lca(a,b)||lca(l ...

随机推荐

  1. html 中的列表

    html 中列表可以分为 1.  无序列表(ul--li 的形式) 2.  有序列表(ol li的形式) 3.  定义列表(dl 的形式) 下面来看几种列表的具体内容: 1.无序列表. 无序列表的格式 ...

  2. i8042 键盘控制器-------详细介绍

    [转]http://shanzy.bokee.com/834368.html ps/2 键盘硬件概述 对于驱动来说,和键盘相关的最重要的硬件是两个芯片.一个是 intel 8042 芯片,位于主板上, ...

  3. 【项目管理】git和码云的使用【转】

    转自:https://www.cnblogs.com/riverdubu/p/6491944.html 缘起 说了那么多关于git和码云相关的事,一直都没给大伙讲解这个码云究竟是个啥玩意儿. 今天就给 ...

  4. dm368 ipnc3.0环境搭建脚本

    前言 为了方便其他人搭建dm368 ipnc3.0环境,我写了个脚本,执行脚本就可以自动搭建好环境了,绝对的傻瓜操作了,不过有一个地方让我很郁闷,那就是在用sed替换掉某段内容的时候(143行--15 ...

  5. 《30天自制操作系统》笔记(01)——hello bitzhuwei’s OS!【转】

    转自:http://www.cnblogs.com/bitzhuwei/p/OS-in-30-days-01-hello-bitzhuwei-OS.html 阅读目录(Content) 最初的OS代码 ...

  6. 有关mysql的innodb_flush_log_at_trx_commit参数【转】

    一.参数解释 0:log buffer将每秒一次地写入log file中,并且log file的flush(刷到磁盘)操作同时进行.该模式下在事务提交的时候,不会主动触发写入磁盘的操作. 1:每次事务 ...

  7. linux系统定时任务设置

    .使用at命令设置一次性定时任务 2.使用crontab设置周期性定时任务 1)cd /home 目录下,使用vi test.py创建文件,内容如下: #!/usr/bin/python#coding ...

  8. 浅谈Java中的hashcode方法(转)

    原文链接:http://www.cnblogs.com/dolphin0520/p/3681042.html 浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地 ...

  9. [MySQL]You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column

    执行update语句,出现问题: 问题描述: You are using safe update mode and you tried to update a table without a WHER ...

  10. Apache配置实现日志按天分割并删除指定几天前的日志

    Apache日志默认情况下是一周切割一次,由于访问量大的时候日志的文件还是比较大的,同时也不利于管理员对日志的分析处理.于是尝试对Apache日志设置按天分割,并通过计划任务执行删除几天的日志. 配置 ...