JZOJ 6276. 【noip提高组模拟1】树
题目
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 > <1,2>,<1,3>,<1,4>,<1,5>,<1,6>,<1,7>,<2,4>,<2,5>,<3,6>,<3,7>,<4,5> <1,2>,<1,3>,<1,4>,<1,5>,<1,6>,<1,7>,<2,4>,<2,5>,<3,6>,<3,7>,<4,5>。
题解
- 观察题目,易得答案为总路径条数(不重复)减去不符合条件的路径条数,
- 对于一个 < x , y > <x,y> <x,y>,令其 d e e p [ x ] ≥ d e e p [ y ] deep[x]≥deep[y] deep[x]≥deep[y]
- 情况一:若 y y y是 x x x的祖先,则 x x x的子树内的所有点与 n n n个点中除了 y y y的子树以外的所有点之间相互的路径都是不合法的,因为路径必定经过 x x x与 y y y;
- 情况二:若 x x x与 y y y没有祖先关系,则 x x x的子树内的所有点与 y y y的子树内的所有点之间相互的路径都是不合法的,它们必定经过 x x x与 y y y。
- 但对于不同的 < x , y > <x,y> <x,y>,这些路径会有重复,所以不能单纯地直接计算,那怎么办呢?
- 先把 n n n个点的 D F S DFS DFS序求出来,把不能互相连通的两个区间分别作为矩形的横纵坐标位置,将其覆盖在坐标系中,
- 如 [ x 1 , x 2 ] [x1,x2] [x1,x2](这是一个区间)与 [ y 1 , y 2 ] [y1,y2] [y1,y2]不能相互到达,那么在 x 1 x1 x1处, [ y 1 , y 2 ] [y1,y2] [y1,y2]区间打上 + 1 +1 +1标记,在 x 2 + 1 x2+1 x2+1处, [ y 1 , y 2 ] [y1,y2] [y1,y2]区间打上 − 1 -1 −1标记,
- 那么对应的区间怎么表示呢?
- 情况一:分两个区间,情况二:分一个区间。(请自行思考,用 d f n [ ] dfn[] dfn[]和 s i z e [ ] size[] size[])
- 然后扫描线从左往右扫,用线段树区间修改维护,因为每个加入的区间,后面必定会对应的删除,所以这是标记不下传的区间修改,
- 记录 s [ v ] s[v] s[v]表示线段树上当前区间整段被覆盖的次数, f [ v ] f[v] f[v]为整个区间未被覆盖的总长度,
- 每次 + 1 +1 +1时, s [ v ] + + s[v]++ s[v]++, f [ v ] = 0 f[v]=0 f[v]=0,
- 每次 − 1 -1 −1时, s [ v ] − − s[v]-- s[v]−−,当 s [ v ] = 0 s[v]=0 s[v]=0时, f [ v ] = f [ l ] + f [ r ] f[v]=f[l]+f[r] f[v]=f[l]+f[r](由左右子树更新自己的值),
- 同时回溯时更新 f [ v ] = f [ l ] + f [ r ] f[v]=f[l]+f[r] f[v]=f[l]+f[r]。
- a n s ans ans每次累加 [ i + 1 , n ] [i+1,n] [i+1,n]的答案(这样才不会重复)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
#define N 100010
int last[N],next[N*2],to[N*2],len=0;
int dfn[N],si[N],dp[N],f[N][20];
int qs=0,bz[N*4];
LL g[N*4],s=0;
struct node
{
int x,l,r,c;
}q[N*4];
void add(int x,int y)
{
to[++len]=y;
next[len]=last[x];
last[x]=len;
}
void dfs(int k,int fa)
{
dp[k]=dp[fa]+1,si[k]=1,dfn[k]=++dfn[0];
f[k][0]=fa;
for(int i=1;i<20;i++) f[k][i]=f[f[k][i-1]][i-1];
for(int i=last[k];i;i=next[i]) if(to[i]!=fa)
{
dfs(to[i],k);
si[k]+=si[to[i]];
}
}
void insert(int x,int xx,int y,int yy)
{
if(x>xx) return;
if(y>yy) return;
if(y<x) swap(x,y),swap(xx,yy);
q[++qs].x=x,q[qs].l=y,q[qs].r=yy,q[qs].c=1;
q[++qs].x=xx+1,q[qs].l=y,q[qs].r=yy,q[qs].c=-1;
}
int cmd(node x,node y)
{
return x.x<y.x;
}
void make(int v,int l,int r,int x,int y,int c)
{
if(l==x&&r==y)
{
bz[v]+=c;
if(bz[v]) g[v]=r-l+1;
if(!bz[v])
{
if(l!=r) g[v]=g[v*2]+g[v*2+1]; else g[v]=0;
}
}
else
{
int mid=(l+r)/2;
if(y<=mid) make(v*2,l,mid,x,y,c);
else if(x>mid) make(v*2+1,mid+1,r,x,y,c);
else make(v*2,l,mid,x,mid,c),make(v*2+1,mid+1,r,mid+1,y,c);
if(!bz[v]) g[v]=g[v*2]+g[v*2+1];
}
}
void find(int v,int l,int r,int x,int y)
{
if(l==x&&r==y)
{
s+=r-l+1-g[v];
}
else
{
int mid=(l+r)/2;
if(y<=mid) find(v*2,l,mid,x,y);
else if(x>mid) find(v*2+1,mid+1,r,x,y);
else find(v*2,l,mid,x,mid),find(v*2+1,mid+1,r,mid+1,y);
}
}
int main()
{
int n,m,i,j,x,y;
scanf("%d%d",&n,&m);
for(i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dfs(1,0);
for(i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
if(dp[x]<dp[y]) swap(x,y);
int tx=x;
if(dp[x]==dp[y])
{
insert(dfn[x],dfn[x]+si[x]-1,dfn[y],dfn[y]+si[y]-1);
continue;
}
for(j=19;j>=0;j--) if(dp[x]-(1<<j)>dp[y]) x=f[x][j];
int tp=x;
x=f[x][0];
if(x==y)
{
x=tx;
insert(dfn[x],dfn[x]+si[x]-1,1,dfn[tp]-1);
insert(dfn[x],dfn[x]+si[x]-1,dfn[tp]+si[tp],n);
}
else
{
x=tx;
insert(dfn[x],dfn[x]+si[x]-1,dfn[y],dfn[y]+si[y]-1);
}
}
sort(q+1,q+qs+1,cmd);
j=1;
for(i=1;i<n;i++)
{
while(j<=qs&&q[j].x==i)
{
make(1,1,n,q[j].l,q[j].r,q[j].c);
j++;
}
find(1,1,n,i+1,n);
}
printf("%lld
",s);
return 0;
}