NOIP模拟 相遇

【题目描述】

豪哥生活在一个n个点的树形城市里面,每一天都要走来走去。虽然走的是比较的多,但是豪哥在这个城市里面的朋友并不是很多。

当某一天,猴哥给他展现了一下大佬风范之后,豪哥决定要获得一些交往机会来提升交往能力。豪哥现在已经物色上了一条友,打算和它(豪哥并不让吃瓜群众知道性别)交往。豪哥现在spy了一下这个人的所有行程起点和终点,豪哥打算从终点开始走到起点与其相遇。但是豪哥是想找话题的,他想知道以前有多少次行程和此次行程是有交集的,这样豪哥就可以搭上话了。这个路径与之前路径的有交集数量作为豪哥此次的交往机会。

但是豪哥急着要做交往准备,所以算什么交往机会的小事情就交给你了。

【输入格式】

第一行一个正整数n表示节点个数。接下来n-1行,每行两个正整数分别是u,v表示节点u和v之间有连边。接下来一行一个 正整数m表示路径个数。然后有m行,每行两个正整数分别是u,v分别表示u到v之间有一条路径。

【输出格式】

输出共m行,每行一个整数,第i行表示豪哥在这条路径上获得的交往机会。

【样例输入】

5

1 2

1 3

3 4

3 5

4

4 5

4 2

1 3

1 2

【样例输出】

0

1

2

2

【备注】

对于20%的数据n,m≤2000

对于另外20%的数据n,m≤50000

对于另外10%的数据n,m≤200000保证树形结构是一条链

对于另外50%的数据n,m≤200000

【题目分析】

(口胡开始)

我们先找一下对于两条路径(a,b)和(c,d),如果他们有交集有什么规律?

不难发现,如果两条路径有交集,那么对于lca(a,b)和lca(c,d),要么lca(a,b)在路径(c,d)上,要么lca(c,d)在(a,b)上,要么两个lca相同,证明就一句话,感性理解:假设我们现在手上是一条 lca 深度大的路径,这条路径最高可达点就是 lca,若 lca不在另一条路径上,又因为他lca深度大,所以不可能在另外一条路径 lca上方,所以就没有可达点了。

所以这个问题就转化为两个问题:1.当前路径上有几个之前的lca;2.之前路径有几个经过当前路径的lca;(当然要特判一下lca相同的时候)

1.当前路径上找lca

因为这是一颗静态树,所以考虑用dfs序来解决。对于一个点x,a->lca(a,b)这样的树链如果经过他,则一定是从它的子树走向它的祖先。  然后就比较显然了,每多加一个这样的x,则给他子树中所有点的权值都+1.  查询(a,lca(a,b),b)的时候,就用 a权+b权-lca权*2.  若 a与lca在x的上下两方,则会被累加到贡献。若同时在x的下方,则会被 lca权减掉。

2.在之前路径上找lca

同样地考虑dfs序,  很方便的树上前缀和。若有一条路径(a,b),则给lca(a,b) +1,a-1,b-1.然后查询一个点到根的权值和,这是离线时的套路。 但这题这样不好做,因为不能每一次都重构树,于是我们变一下,变成类似树上后缀和的东西。  

对于一条路径(a,b),给a+1,b+1,lca-2. 一个点的子树权值和就是他的权值。 

用两树状数组维护一下dfs 序上的信息,我们就把这道题给搞定了。  

这题的坑点主要在于,两个树状数组维护的信息意义是不一样的,压根不可以相提并论。

【代码~】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
#define lowbit(x) ((x)&(-x))
#define treesum(t,x) (sum(t,r[x])-sum(t,l[x]-1))

int n,m,stm,cnt;
int head[MAXN],depth[MAXN];
int l[MAXN],r[MAXN];
int to[MAXN<<1],nxt[MAXN<<1];
int f[MAXN][19];
int t1[MAXN],t2[MAXN],ans;
int app[MAXN];

void change(int *tr,int x,int v)
{
    for(;x<=n;x+=lowbit(x))
      tr[x]+=v;
}

int sum(int *tr,int x)
{
    int ret=0;
    for(;x;x-=lowbit(x))
      ret+=tr[x];
    return ret;
}

void add(int x,int y)
{
    cnt++;
    nxt[cnt]=head[x];
    head[x]=cnt;
    to[cnt]=y;
}

void dfs(int u,int fa)
{
    l[u]=++stm;
    f[u][0]=fa;
    for(int i=1;i<19;++i)
      f[u][i]=f[f[u][i-1]][i-1];
    depth[u]=depth[fa]+1;
    for(int i=head[u];i!=-1;i=nxt[i])
    {
        int v=to[i];
        if(v!=fa)
        {
            dfs(v,u);
        }
    }
    r[u]=stm;
}

int lca(int x,int y) 
{   
    if (depth[x]<depth[y]) 
      swap(x,y);   
    for(int i=18;i>=0;i--) 
      if(depth[f[x][i]]>=depth[y]) 
        x=f[x][i];   
    if(x==y) 
      return x;   
    for(int i=18;i>=0;i--) 
      if(f[x][i]!=f[y][i]) 
        x=f[x][i],y=f[y][i];    
    return f[x][0];   
}   

void calcans(int x,int y,int lc)
{
    ans+=treesum(t1,lc);
    ans+=sum(t2,l[x])+sum(t2,l[y])-sum(t2,l[lc])*2;
}

int main()
{
    memset(head,-1,sizeof(head));
    memset(nxt,-1,sizeof(nxt));
    scanf("%d",&n);
    for(int i=1;i<n;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    dfs(1,-1);
    scanf("%d",&m);
    for(int i=1;i<=m;++i)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int lc=lca(x,y);
        ans=0;
        calcans(x,y,lc);
        printf("%d\n",ans+app[lc]);
        app[lc]++;
        change(t1,l[x],1);   
          change(t1,l[y],1);   
          change(t1,l[lc],-2);   
          change(t2,l[lc],1);   
          change(t2,r[lc]+1,-1);
    }
    return 0;
}

 

posted @ 2018-10-05 16:30  Ishtar~  阅读(121)  评论(0编辑  收藏  举报