SP10707 COT2 - Count on a tree II

本文介绍了如何使用树上莫队算法解决一个树形结构的问题,即在给定的树中,从节点u到节点v的路径上不同整数的数量。文章详细阐述了输入输出格式,并强调了将DFS序改为欧拉序的策略,同时提到了在处理路径时的关键点,如节点遍历顺序和LCA的计算。此外,还提及了比较函数中的一处优化技巧。
摘要由CSDN通过智能技术生成

树上莫队
题目描述
给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数。
输入格式
第一行有两个整数n和m(n=40000,m=100000)。
第二行有n个整数。第i个整数表示第i个节点表示的整数。
在接下来的n-1行中,每行包含两个整数u v,描述一条边(u,v)。
在接下来的m行中,每一行包含两个整数u v,询问u到v的路径上有多少个不同的整数。
输出格式
对于每个询问,输出结果。
参考题解莫队

其实树上莫队和树链剖分的思想是一样的,就是把树映射到,正常的dfs序,使得每棵子树的所有节点为一个连续的区间,我们可以队这个连续的区间进行区间操作就相当于是对数的以可子树进行操作,树上莫队除了处理子树,还可以处理路径,这道题就是处理路径的,关键点在于把dfs序改成欧拉序,需要额外考虑一些信息,比如两个节点的遍历顺序以及两个节点的lca等。
比较函数中有一处玄学优化。

关于被倍增法求LCA

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e5+10;

ll a[maxn];
ll b[maxn];
ll pos[maxn];//相当于a离散化后的值
ll head[maxn];
ll depth[maxn];
ll fa[maxn][30];
ll ord[maxn<<1];
ll be[maxn<<1];
ll first[maxn];
ll last[maxn];
ll cnt[maxn];
ll ans[maxn];
bool vis[maxn];
ll cnte,cnto;
ll sz;
ll l=1,r=0,now=0;

struct Edge{
    ll v,next;
}e[maxn<<1];

void add(ll u,ll v){
    e[cnte].v=v;
    e[cnte].next=head[u];
    head[u]=cnte++;
}

inline ll read() {
	ll res = 0;
	char c = getchar();
	while(!isdigit(c)) c = getchar();
	while(isdigit(c)) res = (res << 1) + (res << 3) + (c ^ 48), c = getchar();
	return res;
}

void dfs(ll u){//预处理fa和depth数组为求LCA初始化,并求欧拉序
    ord[++cnto]=u;
    be[cnto]=cnto/sz;
    first[u]=cnto;
    for(ll i=head[u];~i;i=e[i].next){
        ll v=e[i].v;
        if(v==fa[u][0])continue;
        depth[v]=depth[u]+1;
        fa[v][0]=u;
        for(ll i=1;(1<<i)<=depth[v];i++){
            fa[v][i]=fa[fa[v][i-1]][i-1];
        }
        dfs(v);
    }
    ord[++cnto]=u;
    be[cnto]=cnto/sz;
    last[u]=cnto;
}

ll LCA(ll u,ll v){
    if(depth[u]<depth[v])
        swap(u,v);
    for(ll i=20;i>=0;i--){
        if(depth[u]-(1<<i)>=depth[v])
            u=fa[u][i];
    }
    if(u==v)return u;
    for(ll i=20;i>=0;i--){
        if(fa[u][i]!=fa[v][i]){
            u=fa[u][i];
            v=fa[v][i];
        }
    }
    return fa[u][0];
}

struct Q{
    ll l,r,id,lca;
    friend bool operator <(Q a,Q b){
        return (be[a.l] ^ be[b.l]) ? (be[a.l] < be[b.l]) : ((be[a.l] & 1) ? a.r < b.r : a.r > b.r);
    }
}q[maxn];

void work(ll x){
    if(vis[x]){
        cnt[pos[x]]--;
        if(cnt[pos[x]]==0)
            now--;
    }
    else{
        if(cnt[pos[x]]==0)
            now++;
            cnt[pos[x]]++;
    }
    vis[x]=(!vis[x]);
}

int main(){
    memset(head,-1,sizeof(head));
    ll m,n;
    n=read(),m=read();
    for(ll i=1;i<=n;i++){
        a[i]=read();
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    ll num=unique(b+1,b+1+n)-b-1;
    for(ll i=1;i<=n;i++){
        pos[i]=lower_bound(b+1,b+1+n,a[i])-b;
    }
    for(ll i=1;i<n;i++){
        ll u,v;
        u=read(),v=read();
        add(u,v);
        add(v,u);
    }
    sz=sqrt(2*n);
    depth[1]=1;
    fa[1][0]=-1;
    dfs(1);
    for(ll i=1;i<=m;i++){
        ll u,v;
        u=read(),v=read();
        q[i].id=i;
        ll lca=LCA(u,v);
        if(first[u]>first[v])
            swap(u,v);
        if(u==lca){
            q[i].l=first[u];
            q[i].r=first[v];
            q[i].lca=0;
        }
        else{
            q[i].l=last[u];
            q[i].r=first[v];
            q[i].lca=lca;
        }
    }
    sort(q+1,q+1+m);
    for(ll i=1;i<=m;i++){
        ll ql=q[i].l,qr=q[i].r,lca=q[i].lca;
		while(l < ql) work(ord[l++]);
		while(l > ql) work(ord[--l]);
		while(r < qr) work(ord[++r]);
		while(r > qr) work(ord[r--]);
		if(lca) work(lca);
		ans[q[i].id] = now;
		if(lca) work(lca);
    }
    for(ll i=1;i<=m;i++){
        printf("%lld\n",ans[i]);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值