Count on a tree II(树上莫队)

Posted hsez-cyx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Count on a tree II(树上莫队)相关的知识,希望对你有一定的参考价值。

Count on a tree II(luogu)

Description

题目描述

给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数。

输入格式

第一行有两个整数n和m(n=40000,m=100000)。

第二行有n个整数。第i个整数表示第i个节点表示的整数。

在接下来的n-1行中,每行包含两个整数u v,描述一条边(u,v)。

在接下来的m行中,每一行包含两个整数u v,询问u到v的路径上有多少个不同的整数。

输出格式

对于每个询问,输出结果。

Solution

树上莫队模板

先按照欧拉序(两次访问的时间,此处不赘述了)对树上的节点分块

当询问为x,y时(设st[x]<st[y])

(i)x为y的祖先

访问区间为st[x]-st[y]

(ii)x不为y的祖先

访问区间为ed[x]-st[y]

解释:

  • 因为x,y都在lca的子树上且x比y先被访问,所以访问完x回溯后再访问y,即ed[x]<st[y]
  • 先将x-lca上的点访问一遍(ed[x]-ed[lca至x路径上的son]),再将lca至y上的点访问一遍(st[lca至y路径上的son]-st[y])
  • 由于st[lca]<ed[x] && st[y]<ed[lca],所以lca没有被算进去,要特判

还有一件特别重要的事:

这题非要用树剖求lca!!!(zz,卡我好久...)

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N=1e5+10,M=4e4+10;
int ans[N],an,s_d[M],s_u[M],u,v,n,m,d[M],cnt,tot,pos[N],st[M],ed[M],si,bl,fa[M],dep[M],b[N];
int top[M],son[M],siz[M];
vector <int> link[N];
struct node
{
    int l,r,id,lca;
    bool operator <(const node &o)const
    {
        return pos[l]^pos[o.l]?l<o.l:(pos[l]&1?r<o.r:r>o.r);
    }
}q[N];
struct mode
{
    int v,id;
    bool operator <(const mode &o)const
    {
        return v<o.v;
    }
}a[M];
void dfs(int u,int fx)
{
    st[u]=++cnt,b[cnt]=u;
    fa[u]=fx,dep[u]=dep[fx]+1;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v!=fx) dfs(v,u);
    }
    ed[u]=++cnt,b[cnt]=u;
}
void dfs1(int u)
{
    siz[u]=1;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v==fa[u]) continue;
        dfs1(v),siz[u]+=siz[v];
        if(son[u]==0 || siz[v]>siz[son[u]]) son[u]=v;
     }
}
void dfs2(int u)
{
    if(son[u]) top[son[u]]=top[u],dfs2(son[u]);
    else return ;
    int size=link[u].size();
    for(int i=0;i<size;i++)
    {
        int v=link[u][i];
        if(v==fa[u] || v==son[u]) continue;
        top[v]=v,dfs2(v);
    }
}
int Lca(int x,int y)
{
    int px=top[x],py=top[y];
    while(px!=py)
        if(dep[px]>dep[py])
            x=fa[px],px=top[x];
        else y=fa[py],py=top[y];
    if(dep[x]>dep[y]) return y;
    else return x;
}
void get(int x,int y)
{
    if(x==0) return;
    s_u[x]+=y;
    if(s_u[x]&1)
    {
        s_d[d[x]]++;
        if(s_d[d[x]]==1) an++;
    }
    else
    {
        s_d[d[x]]--;
        if(s_d[d[x]]==0) an--;
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i].v),a[i].id=i;
    sort(a+1,a+1+n);
    for(int i=1;i<=n;i++)
    {
        if(i==1 || a[i].v!=a[i-1].v) cnt++;
        d[a[i].id]=cnt;
    }
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&u,&v);
        link[u].push_back(v);
        link[v].push_back(u);
    }
    cnt=0,dfs(1,0);
    dfs1(1),top[1]=1,dfs2(1);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&u,&v);
        if(st[u]>st[v]) swap(u,v);
        int w=Lca(u,v);
        if(w==u) q[i].l=st[u],q[i].r=st[v],q[i].lca=0;
        else q[i].lca=d[w],q[i].l=ed[u],q[i].r=st[v];
        q[i].id=i;
    }
    n=cnt;
    si=sqrt(n);
    bl=(n+si-1)/si;
    for(int i=1;i<=bl;i++)
    {
        int z=min(n,i*si);
        for(int j=(i-1)*si+1;j<=z;j++)
            pos[j]=i;
    }
    sort(q,q+m);
    int nl=0,nr=0;
    for(int i=0;i<m;i++)
    {
        while(nl>q[i].l) get(b[--nl],1);
        while(nl<q[i].l) get(b[nl++],-1);
        while(nr>q[i].r) get(b[nr--],-1);
        while(nr<q[i].r) get(b[++nr],1);
        ans[q[i].id]=an;
        if(q[i].lca) ans[q[i].id]+=(s_d[q[i].lca]==0);
    }
    for(int i=0;i<m;i++)
        printf("%d
",ans[i]);
    return 0;
}

 

以上是关于Count on a tree II(树上莫队)的主要内容,如果未能解决你的问题,请参考以下文章

SPOJ.COT2 Count on a tree II(树上莫队)

SP10707 COT2 - Count on a tree II (树上莫队)

SPOJ10707 COT2 - Count on a tree II 树上莫队

SPOJ COT2 - Count on a tree II(树上莫队)

SPOJ10707COT2 - Count on a tree II

「SPOJ10707」Count on a tree II