1. 程式人生 > 其它 >【jzoj 6276】樹(線段樹)(掃描線)

【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;
}