[扫描线][倍增][dfs序][线段树] Jzoj P6276 树

Description

有一棵n个节点的无根树,给出其中的m对点对<x,y>。问有多少条树上的简单路径<u,v>满足该路径上不存在任何一对给出的点对<x,y>。
这里我们认为路径<u,v>和<v,u>是相同的。并且对于题目中给出的点对<x,y>满足x!=y,对于你要计数的路径<u,v>满足u!=v(即单点不算答案)。

题解

  • 我们先把每个点在dfs上入队出队的时间给弄出来
  • 然后可以转换为在平面上的n个矩阵
  • 就可以用扫描线+线段树给过掉它了

代码

 1 #include <cstdio>
 2 #include <algorithm>
 3 #define N 100010
 4 #define ll long long
 5 using namespace std;
 6 struct edge { ll to,from; }e[N*2];
 7 struct Edge{ ll l,r,x,k; }a[N*40];
 8 ll n,m,tot,cnt,num,ans,p=1,fa[17][N],head[N],d[N*4],tag[N*4],dfn[N][2],t[N*4];
 9 bool cmp(Edge a,Edge b) { return a.x<b.x; }
10 void insert(ll x,ll y)
11 {
12     e[++cnt].to=y,e[cnt].from=head[x],head[x]=cnt;
13     e[++cnt].to=x,e[cnt].from=head[y],head[y]=cnt;
14 }
15 void ins(ll x1,ll x2,ll y1,ll y2) { a[++num]=(Edge){x1,x2,y1,1},a[++num]=(Edge){x1,x2,y2+1,-1}; }
16 void dfs(ll x)
17 {
18     dfn[x][0]=++tot;
19     for (ll i=head[x];i;i=e[i].from) if (!dfn[e[i].to][0]) dfs(e[i].to),fa[0][e[i].to]=x;
20     dfn[x][1]=tot;
21 }
22 void add(ll d,ll l,ll r,ll L,ll R,ll x)
23 {
24     if (l==L&&r==R)
25     {
26         tag[d]+=x;
27         if (l==r) t[d]=(tag[d]>0); else if (tag[d]) t[d]=r-l+1; else t[d]=t[d*2]+t[d*2+1];
28         return;
29     }
30     ll mid=l+r>>1;
31     if (R<=mid) add(d*2,l,mid,L,R,x); else if (L>mid) add(d*2+1,mid+1,r,L,R,x);
32     else add(d*2,l,mid,L,mid,x),add(d*2+1,mid+1,r,mid+1,R,x);
33     if (tag[d]) t[d]=r-l+1; else t[d]=t[d*2]+t[d*2+1];
34 }
35 int main()
36 {
37     freopen("tree.in","r",stdin),freopen("tree.out","w",stdout),scanf("%lld%lld",&n,&m);
38     for (ll i=1,x,y;i<n;i++) scanf("%lld%lld",&x,&y),insert(x,y);
39     dfs(1);
40     for (ll i=1;i<=16;i++) for (ll j=1;j<=n;j++) fa[i][j]=fa[i-1][fa[i-1][j]];
41     for (ll i=1,x,y,x1,y1,x2,y2;i<=m;i++)
42     {
43         scanf("%lld%lld",&x,&y),x1=dfn[x][0],y1=dfn[y][0],x2=dfn[x][1],y2=dfn[y][1];
44         if (x1>y1) swap(x,y),swap(x1,y1),swap(x2,y2);
45         if (x2<y1) ins(x1,x2,y1,y2),ins(y1,y2,x1,x2);
46         else
47         {
48             ll p=y;
49             for (ll j=16;j>=0;j--) if (dfn[fa[j][p]][0]>x1) p=fa[j][p];
50             if (dfn[p][0]>1) ins(1,dfn[p][0]-1,y1,y2),ins(y1,y2,1,dfn[p][0]-1);
51             if (dfn[p][1]!=n) ins(dfn[p][1]+1,n,y1,y2),ins(y1,y2,dfn[p][1]+1,n);
52         }
53     }
54     sort(a+1,a+num+1,cmp);
55     for (ll i=1;i<=n;i++)
56     {
57         while (p<=num&&a[p].x==i) { add(1,1,n,a[p].l,a[p].r,a[p].k),p++; }
58         ans+=t[1];
59     }
60     ans=(n*(n-1)-ans)/2,printf("%lld",ans);
61 }

原文地址:https://www.cnblogs.com/Comfortable/p/11318037.html

时间: 2024-11-09 03:05:49

[扫描线][倍增][dfs序][线段树] Jzoj P6276 树的相关文章

[BZOJ 3306]树(dfs序+线段树+倍增)

Description 给定一棵大小为 n 的有根点权树,支持以下操作: • 换根 • 修改点权 • 查询子树最小值 Solution 单点修改子树查询的话可以想到用dfs序+线段树来处理,换根的处理画一画图应该可以明白: 如果查询的x是当前的根rt,直接返回整棵树的min 如果rt在x的子树中,用倍增的方法找到离x最近的rt的祖先t,整棵树除t的子树以外的部分就是x当前根下的子树 如果rt不在x的子树中,查询x原来的子树的min值 #include<iostream> #include<

POJ 3321 DFS序+线段树

单点修改树中某个节点,查询子树的性质.DFS序 子树序列一定在父节点的DFS序列之内,所以可以用线段树维护. 1: /* 2: DFS序 +线段树 3: */ 4:   5: #include <cstdio> 6: #include <cstring> 7: #include <cctype> 8: #include <algorithm> 9: #include <vector> 10: #include <iostream> 1

codevs1228 (dfs序+线段树)

总结: 第一次遇到dfs序的问题,对于一颗树,记录节点 i 开始搜索的序号 Left[i] 和结束搜索的序号 Righti[i],那么序号在 Left[i] ~ Right[i] 之间的都是节点 i 子树上的节点. 并且此序号与线段树中 L~R 区间对应,在纸上模拟了几遍确实如此,但暂时还未理解为何对应. 此题就是dfs序+线段树的裸题 代码: #include<iostream> #include<vector> #include<cstring> #include&

Educational Codeforces Round 6 E dfs序+线段树

题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili上电子科技大学发的视频学习的 将一颗树通过dfs编号的方式 使每个点的子树的编号连在一起作为相连的区间 就可以配合线段树搞子树 因为以前好像听说过 线段树可以解决一种区间修改和查询区间中不同的xx个数...所以一下子就想到了... 但是我不会写线段树..只会最简单的单点修改区间查询...不会用延迟标

【BZOJ-3252】攻略 DFS序 + 线段树 + 贪心

3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 339  Solved: 130[Submit][Status][Discuss] Description 题目简述:树版[k取方格数] 众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏. 今天他得到了一款新游戏<XX半岛>,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景.所有场景和选择支构成树状结构:开始游戏时在根节点(共通线)

【bzoj3779】重组病毒 LCT+树上倍增+DFS序+树状数组区间修改区间查询

题目描述 给出一棵n个节点的树,每一个节点开始有一个互不相同的颜色,初始根节点为1. 定义一次感染为:将指定的一个节点到根的链上的所有节点染成一种新的颜色,代价为这条链上不同颜色的数目. 现有m次操作,每次为一下三种之一: RELEASE x:对x执行一次感染: RECENTER x:把根节点改为x,并对原来的根节点执行一次感染: REQUEST x:询问x子树中所有节点感染代价的平均值. 输入 输入的第一行包含两个整数n和m,分别代表局域网中计算机的数量,以及操作和询问的总数.接下来n-1行,

【XSY2667】摧毁图状树 贪心 堆 DFS序 线段树

题目大意 给你一棵有根树,有\(n\)个点.还有一个参数\(k\).你每次要删除一条长度为\(k\)(\(k\)个点)的祖先-后代链,问你最少几次删完.现在有\(q\)个询问,每次给你一个\(k\),问你答案是多少. \(n\leq {10}^5,k\leq {10}^9\) 题解 设\(l\)为这棵树的叶子个数,显然当\(k>\)树的深度时答案都是\(l\). 下面要证明:答案是\(O(l+\frac{n-l}{k})\)的. 我们从下往上贪心,每次选择一个未被覆盖的深度最深的点,覆盖这个点网

codeforces 343D Water Tree 树链剖分 dfs序 线段树 set

题目链接 这道题主要是要考虑到同一棵子树中dfs序是连续的 然后我就直接上树剖了... 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=600005; 4 5 struct Node 6 { 7 int l,r; 8 int value; 9 void init() 10 { 11 l=r=value=0; 12 } 13 }tree[4*MAXN]; 14 vector<int>nei[MAXN]

cdoj 574 High-level ancients dfs序+线段树

High-level ancients Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.uestc.edu.cn/#/problem/show/574 Description Love8909 is keen on the history of Kingdom ACM. He admires the heroic undertakings of Lxhgww and Haibo. Inspired by those sagas, L