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 = 0, int nex = 0 ) :v ( v ), nex ( nex ) { }
} Edge[200005];int h[100005], stot;
void add ( int u, int v ) {Edge[++stot] = Node ( v, h[u] );h[u] = stot;
}int fa[100005], siz[100005], son[100005], dep[100005];
void dfs1 ( int u, int f ) {fa[u] = f;    siz[u] = 1;    dep[u] = dep[f] + 1;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[100005], in[100005], 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[400004], tag[400005];
void update ( int nd ) { TR[nd] = TR[nd << 1] + TR[nd << 1 | 1]; }void push_down ( int nd, int l, int r ) {if ( tag[nd] ) {int mid = ( l + r ) >> 1;tag[nd << 1] += tag[nd];tag[nd << 1 | 1] += tag[nd];TR[nd << 1] += tag[nd] * ( mid - l + 1 );TR[nd << 1 | 1] += tag[nd] * ( r - mid );tag[nd] = 0;}
}void add ( int nd, int l, int r, int L, int R, int d ) {if ( l >= L && r <= R ) {TR[nd] += d * ( r - l + 1 );tag[nd] += d;return ;}push_down ( nd, l, r );int mid = ( l + r ) >> 1;if ( L <= mid ) add ( nd << 1, l, mid, L, R, d );if ( R > mid ) add ( nd << 1 | 1, mid + 1, 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 ( 1, 1, n, in[top[u]], in[u], d );u = fa[top[u]];}if ( dep[u] < dep[v] ) swap ( u, v );add ( 1, 1, 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 = 0; int mid = ( l + r ) >> 1;if ( L <= mid ) ans += query ( nd << 1, l, mid, L, R );if ( R > mid ) ans += query ( nd << 1 | 1, mid + 1, r, L, R );return ans;
}int query ( int u, int v ) {int ans = 0;while ( top[u] != top[v] ) {if ( dep[top[u]] < dep[top[v]] ) swap ( u, v );ans += query ( 1, 1, n, in[top[u]], in[u] );u = fa[top[u]];}if ( dep[u] < dep[v] ) swap ( u, v );ans += query ( 1, 1, n, in[v], in[u] );return ans;
}int main ( ) {scanf ( "%d%d", &n, &q );for ( int i = 1; i < n; i ++ ) {int u, v;scanf ( "%d%d", &u, &v );add ( u, v );add ( v, u );}dfs1 ( 1, 0 );dfs2 ( 1, 0 );for ( int i = 1; i <= q; i ++ ) {int a, b, c, d;scanf ( "%d%d%d%d", &a, &b, &c, &d );add ( a, b, 1 );int tmp = query ( c, d );if ( tmp ) printf ( "Y\n" );else printf ( "N\n" );add ( a, b, -1 );}return 0;
}

然而这道题不需要数据结构维护也可以做到,只需要求$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 = 20;int n, q;struct Node {int v, nex;Node ( int v = 0, int nex = 0 ) :v ( v ), nex ( nex ) { }
} Edge[200005];int h[100005], stot;
void add ( int u, int v ) {Edge[++stot] = Node ( v, h[u] );h[u] = stot;
}int jum[100005][P+1], dep[100005];
void dfs ( int u, int f ) {jum[u][0] = f;for ( int i = 1; i <= P; i ++ )jum[u][i] = jum[jum[u][i-1]][i-1];for ( int i = h[u]; i; i = Edge[i].nex ) {int v = Edge[i].v;if ( v == f ) continue;dep[v] = dep[u] + 1;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 = 0; t; t >>= 1, p ++ )if ( t & 1 ) u = jum[u][p];if ( u == v ) return u;for ( int p = P; p >= 0; p -- )if ( jum[u][p] != jum[v][p] ) u = jum[u][p], v = jum[v][p];return jum[u][0];
}int main ( ) {scanf ( "%d%d", &n, &q );for ( int i = 1; i < n; i ++ ) {int u, v;scanf ( "%d%d", &u, &v );add ( u, v );    add ( v, u );}dfs ( 1, 0 );for ( int i = 1; 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 0;
}

转载于:https://www.cnblogs.com/wans-caesar-02111007/p/9561380.html

【树链剖分/倍增模板】【洛谷】3398:仓鼠找sugar相关推荐

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

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

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

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

  3. 专题·树链剖分【including 洛谷·【模板】树链剖分

    初见安~~~终于学会了树剖~~~ [兴奋]当初机房的大佬在学树剖的时候我反复强调过:"学树剖没有前途的!!!" 恩.真香. 一.重链与重儿子 所谓树剖--树链剖分,就是赋予一个链的 ...

  4. 洛谷 P3398 仓鼠找 sugar

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

  5. [Luogu 3398] 仓鼠找sugar

    [Luogu 3398] 仓鼠找sugar 又是 LCA- 前两天死活写不过的一个题今天终于顺手切了. 思路嘛参考了一楼题解. 就是说,对于 a, b, c, d 四个点, 令 x = LCA(a, ...

  6. 树链剖分 讲解+模板+习题

    今天我们来讲一下树链剖分 树链剖分是什么? 树链剖分是一种用来维护树上路径信息的在线方法,可以处理在线. 通常通过一种方法,将一棵树剖分成若干条链,然后通过数据结构(线段树,BIT等)去维护. 我们通 ...

  7. 【BZOJ4568】幸运数字,树链剖分/倍增+维护线性基

    Time:2016.09.06 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 对于两个线性基数组a,b,直接向b中加a的元素进行线性基合并就可以了 复杂度O(P2)O(P^2)其中 ...

  8. CF #563 Div2 F. Ehab and the Big Finale //树链剖分(模板)+ 交互

    题意:给一颗树,和一个隐藏的节点x,在36次询问内求出x. 每次询问: 1.询问x到某一结点u的距离   2.某个节点u到x的路径上,得到u的儿子节点(u必须是x的祖先,否则WA). 思路: 首先处理 ...

  9. 树链剖分 完全模板(子树查改+树链查改)

    题目链接 以洛谷题为原题.看代码. 下面是模板代码(横线之外的为模板代码): /*--------------------1.mchange(x, y, v) 树链更改(x,y) 2.mask(x, ...

  10. 【luogu P3384 树链剖分】 模板

    题目链接:https://www.luogu.org/problemnew/show/P3384 诶又给自己留了个坑..不想写线段树一大理由之前的模板变量名太长 #include <cstdio ...

最新文章

  1. 安装vsftpd-3.0.2.tar.gz源码
  2. forum tribune
  3. QT的QCompleter类的使用
  4. git clone拉取太慢怎么办?
  5. 从底层重学 Java 之两大浮点类型 GitChat连接
  6. w3cschool php 调整图片尺寸,PHP_php修改上传图片尺寸的方法,本文实例讲述了php修改上传图 - phpStudy...
  7. V8 引擎是如何工作的?
  8. Delphi 播放wav声音
  9. 软件工程-第2章复习总结
  10. Linux常见查看日志命令
  11. 每日一淘洞察市场消费真相,深挖三四五线城市消费者需求
  12. ROS新手问题 [rospack] Error: package '***' not found
  13. Java多线程篇--基本概念
  14. 如何利用eclipse创建一个java web项目?
  15. 二、Nio之Channel
  16. 基于JAVA的洗衣店订单管理系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  17. JS(javascript) 将网站加入收藏夹
  18. mysql 存储年月_mysql 存储年月
  19. 巧破网页禁用鼠标右键
  20. 如何通过Tik Tok月入2w美金-跨境知道

热门文章

  1. String和StringBuffer与StringBuilder的区别
  2. Codecraft-17 and Codeforces Round #391 (Div. 1 + Div. 2, combined)
  3. Mirror--自增键在镜像中的影响
  4. [刷ROM] 一米ROM_V3.1版,4.04自用终极珍藏美化版
  5. 提高效率: Atom ,介绍几个实用插件,组合使用
  6. 面试必备!Kafka 怎么顺序消费?
  7. 阿里开源代码质量检测工具!
  8. “12306”是如何支撑百万QPS的?
  9. 30岁以上开发工程师,无法说出的悲哀与迷茫!
  10. 十亿级同步,百亿级调用,千亿级访问量的开放技术平台如何炼成?