SPOJ COT2 Count on a treeII 树上莫队

Problem

SPOJ
求u到v的路径上有多少种不同的点权。

Solution

半年前挖的坑,现在来填了……

我们已经会序列莫队了,那么如果能把树上问题转成序列问题,问题就解决了,那就是欧拉序!

我们不妨记i第一次出现位置为in[i],第二次为out[i]。那么在询问区间中出现了两次的数必定是不会产生贡献的,根据欧拉序的性质,x和y之间路径不会经过它。则用一个桶记录编号为i的节点已经出现了几次,出现两次的节点需要减去它的贡献。

那么有这么两种情况:

  • …x…x…y…y…询问[out[x],in[y]]
  • …x…y…y…x…询问[in[x],in[y]]

但是仔细想想欧拉序的遍历方法,好像又有点不对,因为在第一种情况下,lca并不会出现在它们两之间的欧拉序列之中,特判加入即可。

Code

#include <algorithm>
#include <cstdio>
#include <cmath>
#define rg register
#define chkmid(l,m,r) ((l)<=(m)&&(m)<=(r))
using namespace std;
typedef long long ll;
const int maxn=40010,maxm=100010;
template <typename Tp> inline void getmin(Tp &x,Tp y){if(y<x) x=y;}
template <typename Tp> inline void getmax(Tp &x,Tp y){if(y>x) x=y;}
template <typename Tp> inline void read(Tp &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
    if(ch=='-') f=1,ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    if(f) x=-x;
}
struct data{int v,nxt;}edge[maxn<<1];
struct query{
    int l,r,blk,id;
    bool operator < (const query &t)const{return blk==t.blk?r<t.r:blk<t.blk;}
}q[maxm];
int n,m,p,sqr,dfc,tmp,a[maxn],b[maxn],head[maxn],t[maxn],wt[maxn],ans[maxm];
int f[maxn],dep[maxn],sz[maxn],dfn[2][maxn],top[maxn],hs[maxn],xu[maxn<<1];
inline int insert(int u,int v)
{
    edge[++p]=(data){v,head[u]};head[u]=p;
    edge[++p]=(data){u,head[v]};head[v]=p;
}
void dfs1(int x)
{
    sz[x]=1;dep[x]=dep[f[x]]+1;dfn[0][x]=++dfc;xu[dfc]=x;
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v^f[x])
      {
        f[edge[i].v]=x;
        dfs1(edge[i].v);
        sz[x]+=sz[edge[i].v];
        if(sz[edge[i].v]>sz[hs[x]]) hs[x]=edge[i].v;
      }
    dfn[1][x]=++dfc;xu[dfc]=x;
}
void dfs2(int x,int s)
{
    top[x]=s;
    if(hs[x]) dfs2(hs[x],s);
    for(int i=head[x];i;i=edge[i].nxt)
      if(edge[i].v^f[x]&&edge[i].v^hs[x])
        dfs2(edge[i].v,edge[i].v);
}
int getlca(int x,int y)
{
    int fx=top[x],fy=top[y];
    while(fx^fy)
    {
        if(dep[fx]<dep[fy]) swap(x,y),swap(fx,fy);
        x=f[fx];fx=top[x];
    }
    return dep[x]<dep[y]?x:y;
}
void input()
{
    int tot,x,y;
    read(n);read(m);sqr=(int)sqrt((double(n+n)));
    for(rg int i=1;i<=n;i++) read(a[i]),b[i]=a[i];
    sort(b+1,b+n+1);
    tot=unique(b+1,b+n+1)-b-1;
    for(rg int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+tot+1,a[i])-b;
    for(rg int i=1;i<n;i++){read(x);read(y);insert(x,y);}
    dfs1(1);dfs2(1,1);
    for(rg int i=1;i<=m;i++)
    {
        read(x);read(y);
        if(dfn[0][x]>dfn[0][y]) swap(x,y);
        if(chkmid(dfn[0][x],dfn[0][y],dfn[1][x]))
          q[i].l=dfn[0][x],q[i].r=dfn[0][y];
        else q[i].l=dfn[1][x],q[i].r=dfn[0][y];
        if(q[i].l>q[i].r) swap(q[i].l,q[i].r);
        q[i].blk=(q[i].l-1)/sqr+1;q[i].id=i;
    }
    sort(q+1,q+m+1);
}
void update(int x)
{
    if(t[x])
    {
        wt[a[x]]--;
        if(!wt[a[x]]) tmp--;
    }
    else
    {
        if(!wt[a[x]]) tmp++;
        wt[a[x]]++;
    }
    t[x]^=1;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
    #endif
    int l=1,r=0,lca;
    input();
    for(rg int i=1;i<=m;i++)
    {
        while(l>q[i].l) update(xu[--l]);
        while(r<q[i].r) update(xu[++r]);
        while(l<q[i].l) update(xu[l++]);
        while(r>q[i].r) update(xu[r--]);
        lca=getlca(xu[q[i].l],xu[q[i].r]);
        if(lca^xu[q[i].l]&&lca^xu[q[i].r]){update(lca);ans[q[i].id]=tmp;update(lca);}
        else ans[q[i].id]=tmp;
    }
    for(rg int i=1;i<=m;i++) printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值