【jzoj 6276】树(线段树)(扫描线)

题目链接:jzoj 6276

题目大意

给你一棵树,然后给你一些点对,问你有多少条路径满足路径上不存在任何一组给出的点对。
一个点不算路径。

思路

不难看出我们可以把它弄成一两组限制条件,就是一个 dfs 序上的区间上的点都不能和一个 dfs 序区间上的点匹配。
然后分两种情况弄限制条件:(图来自于WYC的博客,懒得画图了)
在这里插入图片描述
在这里插入图片描述

然后你考虑求不合法的,拿全部减去。
也就是你在一个二维平面上,然后用一些矩阵去覆盖,然后问你你覆盖了多少个点。

然后这个我们考虑用扫描线做:
你考虑用一个懒标记,记录整个区间的修改,然后如果它大于 0,那里面所有都大于 0,就直接返回区间长度,否则就看下面两个的和。
然后这么搞就好了。

代码

#include<cstdio> #include<vector> #include<algorithm> #define ll long long using namespace std; struct node { int to, nxt; }e[200001]; int n, m, x, y; int le[100001], KK, deg[100001]; int sz[100001], fa[100001][21]; int dfn[100001], tmp, tot; ll ans; struct lne { int x1, x2, y1, y2; }q[200001]; struct line { int x, y1, y2, op; }qq[200001 * 2]; void add(int x, int y) { e[++KK] = (node){y, le[x]}; le[x] = KK; e[++KK] = (node){x, le[y]}; le[y] = KK; } void dfs(int now, int father) { dfn[now] = ++tmp; deg[now] = deg[father] + 1; sz[now] = 1; fa[now][0] = father; for (int i = le[now]; i; i = e[i].nxt) if (e[i].to != father) { dfs(e[i].to, now); sz[now] += sz[e[i].to]; } } int jump(int to, int x) { for (int i = 20; i >= 0; i--) if (deg[fa[x][i]] > deg[to]) x = fa[x][i]; return x; } bool cmp(line x, line y) { return x.x < y.x; } struct XD_tree { int val[100001 << 2], lzy[100001 << 2]; void up(int now, int l, int r) { if (lzy[now]) val[now] = r - l + 1; else val[now] = val[now << 1] + val[now << 1 | 1]; } void insert(int now, int l, int r, int L, int R, int va) { if (L <= l && r <= R) { lzy[now] += va; if (lzy[now]) val[now] = r - l + 1; else if (l == r) val[now] = 0; else val[now] = val[now << 1] + val[now << 1 | 1]; return ; } int mid = (l + r) >> 1; if (L <= mid) insert(now << 1, l, mid, L, R, va); if (mid < R) insert(now << 1 | 1, mid + 1, r, L, R, va); up(now, l, r); } }T; int main() { // freopen("tree.in", "r", stdin); // freopen("tree.out", "w", stdout); scanf("%d %d", &n, &m); for (int i = 1; i < n; i++) { scanf("%d %d", &x, &y); add(x, y); } dfs(1, 0); for (int i = 1; i <= 20; i++) for (int j = 1; j <= n; j++) fa[j][i] = fa[fa[j][i - 1]][i - 1]; for (int i = 1; i <= m; i++) { scanf("%d %d", &x, &y); if (dfn[x] > dfn[y]) swap(x, y); if (dfn[x] < dfn[y] && dfn[x] + sz[x] - 1 >= dfn[y]) {//有祖先关系,分成两个部分 int z = jump(x, y); if (dfn[z] != 1) q[++tot] = (lne){1, dfn[z] - 1, dfn[y], dfn[y] + sz[y] - 1}; if (dfn[z] + sz[z] - 1 != n) q[++tot] = (lne){dfn[y], dfn[y] + sz[y] - 1, dfn[z] + sz[z], n}; } else { q[++tot] = (lne){dfn[x], dfn[x] + sz[x] - 1, dfn[y], dfn[y] + sz[y] - 1}; } } int plt = 0; for (int i = 1; i <= tot; i++) { if (q[i].x1 > q[i].x2) swap(q[i].x1, q[i].x2); if (q[i].y1 > q[i].y2) swap(q[i].y1, q[i].y2); qq[++plt] = (line){q[i].x1, q[i].y1, q[i].y2, 1}; qq[++plt] = (line){q[i].x2 + 1, q[i].y1, q[i].y2, -1}; } tot = plt; sort(qq + 1, qq + tot + 1, cmp); int noww = 1; for (int x = 1; x <= n; x++) { while (noww <= tot && qq[noww].x == x) { T.insert(1, 1, n, qq[noww].y1, qq[noww].y2, qq[noww].op); noww++; } ans += 1ll * T.val[1]; } printf("%lld", 1ll * n * (n - 1) / 2 - ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/jzoj_6276.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(30)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示