题目描述
题解
对于子树问题,我们可以使用树的 D F S DFS DFS 序来解决这个问题,因为树的DFS序可以十分有利的求解子树相关问题。思考这道题如何用 D F S DFS DFS 来解决。
对于平面直角坐标系来说,以节点编号为横纵坐标,我们可以通过计算如下两个矩形的面积并:
- 以 L u − R u L_u-R_u Lu−Ru 为长, L v − R v L_v-R_v Lv−Rv 为宽。
- 以 L v − R v L_v-R_v Lv−Rv 为长, L u − R u L_u-R_u Lu−Ru 为宽。
这样这些矩形内的点就是所谓有冲突的点。
我们可以用总方案 n ∗ n n*n n∗n减去面积即可。
这道题的代码貌似非常简单,但是我们得到一下结论:
- 子树问题想到 D F S DFS DFS 序。
- 想想面积并的线段数怎么写。
对于线段树而言,用 t a g tag tag 标记它被覆盖了即便了几遍,对于 t a g tag tag 为 0 0 0 的,则对子树求和;否则答案为长度。
代码如下:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2000000;
int n, m, k;
int L[N], R[N], u[N], v[N];
struct node{
int x, y1, y2, v;
friend bool operator < (node p1,node p2) {
return p1.x < p2.x;
}
} opr[N*10];
int read(void)
{
int s = 0, w = 0;char c = getchar();
while (c < '0' || c > '9') w |= c=='-', c = getchar();
while (c >= '0' && c <= '9') s = s*10+c-48, c = getchar();
return w ? -s : s;
}
struct Segment {
int l, r, tag, cnt, len;
} ;
struct SegmentTree
{
Segment a[N*10];
void build(int p,int l,int r)
{
a[p].l = l, a[p].r = r, a[p].len = r-l+1;
if (l == r) return;
int mid = l+r >> 1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
return;
}
void change(int p,int l,int r,int v)
{
if (l <= a[p].l && r >= a[p].r) {
a[p].tag += v;
if (a[p].tag > 0) a[p].cnt = a[p].len;
if (a[p].tag == 0) a[p].cnt = a[p*2].cnt+a[p*2+1].cnt;
return;
}
int mid = a[p].l+a[p].r >> 1;
if (l <= mid) change(p*2,l,r,v);
if (r > mid) change(p*2+1,l,r,v);
if (a[p].tag == 0) a[p].cnt = a[p*2].cnt+a[p*2+1].cnt;
else if (a[p].tag > 0) a[p].cnt = a[p].len;
return;
}
int ask(int p,int l,int r)
{
if (l <= a[p].l && r >= a[p].r) return a[p].cnt;
int mid = a[p].l+a[p].r >> 1, sum = 0;
if (l <= mid) sum += ask(p*2,l,r);
if (r > mid) sum += ask(p*2+1,l,r);
return sum;
}
} tree;
int cnt = 0;
vector < int > E[N];
void dfs(int x,int fa)
{
L[x] = ++cnt;
for (int i=0;i<E[x].size();++i)
{
int y = E[x][i];
if (y == fa) continue;
dfs(y,x);
}
R[x] = ++cnt;
return;
}
int ans = 0;
signed main(void)
{
freopen("jasmine.in","r",stdin);
freopen("jasmine.out","w",stdout);
n = read(), m = read();
for (int x=2;x<=n;++x)
{
int y = read();
E[x].push_back(y);
E[y].push_back(x);
}
dfs(1,0);
for (int i=1;i<=m;++i)
{
u[i] = read(), v[i] = read();
opr[++k] = node{L[u[i]],L[v[i]],R[v[i]],1};
opr[++k] = node{R[u[i]]+1,L[v[i]],R[v[i]],-1};
opr[++k] = node{L[v[i]],L[u[i]],R[u[i]],1};
opr[++k] = node{R[v[i]]+1,L[u[i]],R[u[i]],-1};
}
sort(opr+1,opr+k+1), tree.build(1,1,n*2);
tree.change(1,opr[1].y1,opr[1].y2,opr[1].v);
for (int i=1;i<=n;++i) L[i] ++, R[i] --;
for (int i=2;i<=k;++i)
{
int sum = tree.a[1].cnt;
int dis_x = opr[i].x - opr[i-1].x;
ans += sum * dis_x;
tree.change(1,opr[i].y1,opr[i].y2,opr[i].v);
}
ans += tree.ask(1,1,n*2);
cout<<n*n-ans/2;
return 0;
}