Jzoj P6275 小L的数列___线段树+扫描线+lca
作者:互联网
题目大意:
有一棵n个节点的无根树,给出其中的m对点对<x,y>。问有多少条树上的简单路径<u,v>满足该路径上不存在任何一对给出的点对<x,y>。
这里我们认为路径<u,v>和<v,u>是相同的。并且对于题目中给出的点对<x,y>满足x!=y,对于你要计数的路径<u,v>满足u!=v(即单点不算答案)。
分析:
考虑总方案数减去不合法方案数
以1为根跑出一棵树,并记下dfs序以及子树大小,然后我们分别讨论
一个点对(x,y),设deepy>=deepx
能造成的不合法方案数,
①lca(x,y)=x,
那么x与y路径下的x的儿子z,设dfs序为l,
显然子树y的任意一个点,与除了子树y以及x到y路径上的点以外的其他点配对,都必定产生点对(x,y)
而子树y的dfs序是连续的一段[dfny,dfny+szy−1]
那么与他能配对的点显然就是除了子树z以外的所有点,
那么这些点的dfs序其实就是[1,dfnz−1]和[dfnz+szz,n]
②lca(x,y)!=x
则他们分别位于lca的不同子树中,
那么我们可以发现,不合法的方案就是子树x中的点与子树y中的点配对
就是dfs序为[dfnx,dfnx+szx−1]的点和dfs序为[dfny,dfny+szy−1]的点配对
由于不合法的方案数这样统计可能会出现重复
因为计算过程中我们是根据dfs序的长度(因为dfs序总是连续的一段出现)相乘计算,那么我们可以将这个映射到二维平面上去
即矩形[ax,ay,bx,by]表示dfs序为[ax,ay]的点与dfs序为[bx,by]的点配对
当然不要忘了矩形[bx,by,ax,ay]也要覆盖
那么我们计算他们的交集图形的面积即可,这个用扫描线和线段树解决
因为我们重复覆盖了,最后得到的结果除以2即为不合法方案数
而不考虑有点对时的总方案数就是n∗(n−1)/2
相减即可
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstring>
#include <algorithm>
#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define lson(x) x * 2
#define rson(x) x * 2 + 1
#define N 100005
using namespace std;
typedef long long ll;
struct Code {
int To, nxt;
}e[N*2];
struct Node {
int h, l, r, mazy;
}C[N*5];
struct Note {
int l, r, num, dde;
}a[N*5];
int f[N][35], deep[N], dfn[N], sz[N], ls[N], n, m, cnt, tot, cdp, edr;
ll ans = 0;
void Addedge(int u, int v) {
e[++cnt].To = v, e[cnt].nxt = ls[u], ls[u] = cnt;
e[++cnt].To = u, e[cnt].nxt = ls[v], ls[v] = cnt;
}
void dfs(int x, int father) {
dfn[x] = ++tot;
sz[x] = 1;
f[x][0] = father;
rep(i, 1, 30) f[x][i] = f[f[x][i - 1]][i - 1];
for (int i = ls[x]; i; i = e[i].nxt) {
if (e[i].To == father) continue;
deep[e[i].To] = deep[x] + 1;
dfs(e[i].To, x);
sz[x] += sz[e[i].To];
}
}
int Get_lca(int x, int y) {
rwp(i, 30, 0)
if (deep[f[x][i]] >= deep[y]) x = f[x][i];
if (x == y) return x;
rwp(i, 30, 0)
if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
void Add(int ax, int bx, int ay, int by) {
if (ax > bx || ay > by) return;
C[++edr].h = ay, C[edr].l = ax, C[edr].r = bx, C[edr].mazy = 1;
C[++edr].h = by + 1, C[edr].l = ax, C[edr].r = bx, C[edr].mazy = -1;
C[++edr].h = ax, C[edr].l = ay, C[edr].r = by, C[edr].mazy = 1;
C[++edr].h = bx + 1, C[edr].l = ay, C[edr].r = by, C[edr].mazy = -1;
}
bool cmp(Node aa, Node bb) {
return aa.h < bb.h;
}
void update(int x) {
a[x].num = 0;
if (a[lson(x)].dde > 0) a[x].num += (a[lson(x)].r - a[lson(x)].l + 1); else a[x].num += a[lson(x)].num;
if (a[rson(x)].dde > 0) a[x].num += (a[rson(x)].r - a[rson(x)].l + 1); else a[x].num += a[rson(x)].num;
}
void Build(int x, int l, int r) {
if (l == r) {
a[x].l = l; a[x].r = l; return;
}
int mid = (l + r) >> 1;
Build(lson(x), l, mid);
Build(rson(x), mid + 1, r);
a[x].l = a[lson(x)].l;
a[x].r = a[rson(x)].r;
}
void change(int x, int l, int r, int p, int q, int num) {
if (r < p || l > q) return;
if (p <= l && r <= q) {
a[x].dde += num; return;
}
int mid = (l + r) >> 1;
change(lson(x), l, mid, p, q, num);
change(rson(x), mid + 1, r, p, q, num);
update(x);
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
scanf("%d %d", &n, &m);
int u, v;
rep(i, 1, n - 1) {
scanf("%d %d", &u, &v); Addedge(u, v);
}
deep[1] = 1; dfs(1, 0);
rep(i, 1, m) {
scanf("%d %d", &u, &v);
if (deep[u] < deep[v]) swap(u, v);
int llc = Get_lca(u, v);
if (v == llc) {
int orz = u;
rwp(i, 30, 0)
if (deep[f[orz][i]] >= deep[v] + 1) orz = f[orz][i];
Add(dfn[u], dfn[u] + sz[u] - 1, 1, dfn[orz] - 1);
Add(dfn[u], dfn[u] + sz[u] - 1, dfn[orz] + sz[orz], n);
} else {
Add(dfn[u], dfn[u] + sz[u] - 1, dfn[v], dfn[v] + sz[v] - 1);
}
}
Build(1, 1, n);
sort(C + 1, C + edr + 1, cmp);
int L = 1;
rep(i, 1, n) {
while (C[L].h == i && L <= edr) change(1, 1, n, C[L].l, C[L].r, C[L].mazy), L++;
ans = (ll)ans + a[1].num;
if (L > edr) break;
}
ans = (ll)n * (n - 1) / 2 - ans / 2;
printf("%lld\n", ans);
return 0;
}
标签:Jzoj,int,edr,dfn,P6275,扫描线,ay,ax,bx 来源: https://blog.csdn.net/Gx_Man_VIP/article/details/98777893