[扫描线][线段树]JZOJ 6276 树

Description

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

Input

第一行两个正整数n,m。
接下来n-1行每行两个正整数u,v描述树上的一条边。
接下来m行每行两个正整数x,y描述一对给出的点对。
(注意,这里我们不保证同样的点对<x,y>不会重复出现)

Output

一行一个整数,表示满足要求的树上简单路径的条数。
 

Sample Input

8 3
1 2
1 3
4 8
2 4
2 5
3 6
3 7
2 3
4 8
6 7

Sample Output

11
 

Data Constraint

 

Hint

满足条件的路径为<1,2>,<1,3>,<1,4>,<1,5>,<1,6>,<1,7>,<2,4>,<2,5>,<3,6>,<3,7>,<4,5>。

分析

我们考虑把DFS序处理出来,然后显然,当两个不可互选的点不为祖先关系时,其不合法的路径条数有子树大小的乘积中

我们把这个抽象成二维平面,就是一个点的DFS序和子树中的最大DFS序作为宽,另一个类似作为长的矩形的面积

我们把所有点对都放到平面里,总面积就是不合法方案数,可以用扫描线+线段树处理

祖先关系也类似,只是祖先包含这个儿子的子树不能被选,所以是[1,l[son]-1][r[son]+1,n]的两个边界矩形

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=1e5+10;
struct Line {
    int x,l,r,c;
}h[4*N];
int lcnt;
struct Graph {
    int v,nx;
}g[2*N];
int cnt,list[N],l[N],r[N],tme,f[N][20],dep[N],t[4*N],v[4*N];
int n,m;
long long ans;

void Add(int u,int v) {
    g[++cnt]=(Graph){v,list[u]};list[u]=cnt;
    g[++cnt]=(Graph){u,list[v]};list[v]=cnt;
}

void Addline(int x0,int x1,int y0,int y1) {
    h[++lcnt]=(Line){x0,y0,y1,1};h[++lcnt]=(Line){x1+1,y0,y1,-1};
}

void DFS(int u,int fa) {
    f[u][0]=fa;l[u]=++tme;dep[u]=dep[fa]+1;
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa) DFS(g[i].v,u);
    r[u]=tme;
}

int LCA(int a,int b) {
    if (dep[a]<dep[b]) swap(a,b);
    for (int i=19;i>=0;i--)
        if (dep[f[a][i]]>=dep[b]) a=f[a][i];
    if (a==b) return a;
    for (int i=19;i>=0;i--)
        if (f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i];
    return f[a][0];
}

bool CMP(Line a,Line b) {
    return a.x<b.x;
}

void Change(int x,int l,int r,int ll,int rr,int c) {
    if (r<l||rr<l||r<ll) return;
    if (ll<=l&&r<=rr) {
        t[x]+=c;
        if (t[x]) v[x]=r-l+1;
        else if (l!=r) v[x]=v[x<<1]+v[(x<<1)+1];
        else v[x]=0;
        return;
    }
    int mid=l+r>>1;
    if (ll<=mid) Change(x<<1,l,mid,ll,rr,c);
    if (mid<rr) Change((x<<1)+1,mid+1,r,ll,rr,c);
    v[x]=t[x]?r-l+1:v[x<<1]+v[(x<<1)+1];
}

int main() {
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),Add(u,v);
    DFS(1,0);
    for (int i=1;i<20;i++)
        for (int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1];
    for (int i=1,x,y;i<=m;i++) {
        scanf("%d%d",&x,&y);
        if (l[x]>l[y]) swap(x,y);
        int lca=LCA(x,y);
        if (lca==x) {
            int s=y;
            for (int i=19;i>=0;i--)
                if (dep[f[s][i]]>dep[x]) s=f[s][i];
            if (l[s]-1>0) Addline(1,l[s]-1,l[y],r[y]);
            if (r[s]+1<=n) Addline(l[y],r[y],r[s]+1,n);
        }
        else Addline(l[x],r[x],l[y],r[y]);
    }
    sort(h+1,h+lcnt+1,CMP);
    for (int i=1,j=1;i<=n;i++) {
        while (h[j].x==i&&j<=lcnt) Change(1,1,n,h[j].l,h[j].r,h[j].c),j++;
        ans+=v[1];
    }
    printf("%lld\n",1ll*n*(n-1ll)/2ll-ans);
}
View Code

猜你喜欢

转载自www.cnblogs.com/mastervan/p/11324873.html