【SPOJ COT2】Count on a tree II,树上莫队

本文分享了一次使用树上莫队算法解决特定问题的经历。文章详细介绍了如何将序列问题转化为树上的路径查询问题,并利用DFS序进行优化。此外,还讲解了如何通过ST表求解最近公共祖先(LCA)问题,以及实现过程中的一些技巧。
摘要由CSDN通过智能技术生成

Time:2016.09.07
Author:xiaoyimi
转载注明出处谢谢


传送门
思路:
第一次写树上莫队
被char哥怒裱一通
实际上还是比较简单的
序列的相关维护问题转移到树上一般都要涉及到dfs序
一开始学的时候被恶心了很久,因为去的是盗贴网站,markdown语法都是错误的= =
如果大家要学的话去百度一下就找到了,讲的还是很良心的
核心思想就是(u,v)到(u,v’)的转化只需要走(v,v’)就可以了
还有就是LCA的问题
我这里用的是ST表求LCA
数组可能比较多,看起来稍微有些乱
我就是来写个博文签个到
简直就是差评博文!!
跑的还是挺快的,好像是rank6,不知道有没有被刷下来
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 40005
#define M 100005
using namespace std;
int n,m,tot,S;
int a[N],b[N],block[N],first[N],sum[N],ans[M],dfn[N],fa[N<<1][17],pos[N],num[N<<1],dep[N],F[N];
//dfn表示dfs序序号,sum是每种颜色的个数,fa是ST表,F是每个点的父亲,num存dfs顺序,pos存每个点第一次出现在num中的位置,b用于离散化
bool vis[N];
struct edge{
    int v,next;
}e[N<<1];
struct node{
    int l,r,id;
}q[M];
int in()
{
    int t=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48,ch=getchar();
    return t;
}
int cmp(node a,node b)
{
    if (block[a.l]==block[b.l]) return dfn[a.r]<dfn[b.r];
    return block[a.l]<block[b.l];
}
void add(int x,int y)
{
    e[++tot]=(edge){y,first[x]};first[x]=tot;
    e[++tot]=(edge){x,first[y]};first[y]=tot;
}
void dfs(int x,int f)
{
    dfn[x]=++dfn[0];
    num[++num[0]]=x;
    fa[num[0]][0]=x;
    pos[x]=num[0];
    for (int i=first[x];i;i=e[i].next)
        if (e[i].v!=f)
            dep[e[i].v]=dep[x]+1,
            F[e[i].v]=x,
            dfs(e[i].v,x),
            num[++num[0]]=x,
            fa[num[0]][0]=x;
}
int LCA(int x,int y)
{
    x=pos[x];y=pos[y];
    if (x>y) swap(x,y);
    int p=log2(y-x+1);
    if (dep[fa[x][p]]>dep[fa[y-(1<<p)+1][p]])
        return fa[y-(1<<p)+1][p];
    else
        return fa[x][p];
}
void Point(int x)
{
    if (vis[x]) S-=(--sum[a[x]]==0);
    else S+=(++sum[a[x]]==1);
    vis[x]^=1;
}
void Path(int x,int y)
{
    if (dep[x]<dep[y]) swap(x,y);
    for (;dep[x]!=dep[y];x=F[x])
        Point(x);
    for (;x!=y;x=F[x],y=F[y])
        Point(x),Point(y);
}
main()
{
    n=in();m=in();
    for (int i=1;i<=n;++i) a[i]=b[i]=in();
    sort(b+1,b+n+1);
    b[0]=unique(b+1,b+1+n)-b-1;
    for (int i=1;i<=n;++i)
        a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;
    for (int i=1;i<n;++i)
        add(in(),in());
    dfs(1,0);
    for (int i=1;1<<i<=num[0];++i)
        for (int j=1;j+(1<<i)-1<=num[0];++j)
            if (dep[fa[j][i-1]]>dep[fa[j+(1<<i-1)][i-1]])
                fa[j][i]=fa[j+(1<<i-1)][i-1];
            else
                fa[j][i]=fa[j][i-1];
    int tt=sqrt(n);
    for (int i=1;i<=n;++i)
        block[i]=(dfn[i]-1)/tt+1;
    for (int i=1;i<=m;++i)
    {
        q[i]=(node){in(),in(),i};
        if (dfn[q[i].l]>dfn[q[i].r]) swap(q[i].l,q[i].r);
    }
    sort(q+1,q+m+1,cmp);
    int L=1,R=1;Point(1);
    for (int i=1;i<=m;++i)
    {
        Path(L,q[i].l);
        Path(R,q[i].r);
        Point(LCA(L,R));
        Point(LCA(q[i].l,q[i].r));
        ans[q[i].id]=S;
        L=q[i].l;R=q[i].r;
    }
    for (int i=1;i<=m;++i) printf("%d\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值