• SP10707 COT2 Count on a tree II


    SP10707 COT2 - Count on a tree II

    参考:树上莫队

    树上莫队和普通的莫队差不多,只是把区间从普通的数组,转到欧拉序上(其实也就是括号序)

    该问题求解的是 x,y 两个点之间的最短路径上,有多少个不同颜色的点

    对于这个问题,分两种情况讨论

    1. \(lca(x,y)=x\ or\ y\),我们只需要记录\([b[x],b[y]]\),这段区间的贡献即可,(默认\(b[x]<b[y]\),不然交换)
    2. 若不成立,则记录\(e[x],b[y]\)的贡献

    b[i] 表示 i 这个点 dfs 时开始的时间,e[i] 表示 i 这个点 dfs 结束的时间。

    需要注意的几个点

    • 块的大小为\(\frac{n}{\sqrt{m}}\)时最佳,本题为\(\frac{2n}{\sqrt{m}}\)
    • 在写分块,求 belo 数组的时候,不要写假了,不然复杂度也是假的。
    • 在比较两个点 x,y 的先后次序的时候,要用 b[x] 和 b[y] 来进行比较,不能用 dep 来直接进行比较。
    • dfs 取根的时候,可以用 rand 来随机取根,这样可以防止出题人卡数据。
    //Created by CAD
    #include <bits/stdc++.h>
    using namespace std;
    
    const int maxn=1e5+5;
    int b[maxn],e[maxn],id[maxn<<1],a[maxn];
    int belo[maxn<<1];
    vector<int> g[maxn];
    struct query{
        int l,r,x,y,LCA,id;
        bool operator<(const query& q){
            if(belo[l]!=belo[q.l])  return belo[l]<belo[q.l];
            return (belo[l]&1)?r<q.r:r>q.r;
        }
    }q[maxn];
    int fa[maxn][30],dep[maxn],lg[maxn];
    int tin=0;
    void dfs(int x,int o){
        fa[x][0]=o,dep[x]=dep[o]+1;
        for(int i=1;i<=lg[dep[x]];++i)
            fa[x][i]=fa[fa[x][i-1]][i-1];
        id[b[x]=++tin]=x;
        for(int i:g[x])
            if(i!=o) dfs(i,x);
        id[e[x]=++tin]=x;
    }
    inline int lca(int x,int y){
        if(dep[x]<dep[y]) swap(x,y);
        while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
        if(x==y) return x;
        for(int k=lg[dep[x]]-1;k>=0;--k){
            if(fa[x][k]!=fa[y][k])
                x=fa[x][k],y=fa[y][k];
        }
        return fa[x][0];
    }
    int now=0;
    int ans[maxn],f[maxn],cnt[maxn];
    inline void modify(int &x){
        int num=a[x];
        if(f[x]){
            cnt[num]--;
            if(!cnt[num]) now--;
        }
        else{
            if(!cnt[num]) now++;
            cnt[num]++;
        }
        f[x]^=1;
    }
    
    unordered_map<int,int> vis;
    
    int main(){
        int n,m;
        scanf("%d%d",&n,&m);
        int blo=double(2*n)/sqrt(m);
        for(int i=1;i<=n;++i){
            scanf("%d",a+i);
            if(!vis.count(a[i]))
                vis[a[i]]=vis.size();
            a[i]=vis[a[i]];
            belo[i]=(i-1)/blo+1;
            belo[i*2]=(i*2-1)/blo+1;
    
            lg[i]=lg[i-1]+(1<<lg[i-1]==i);
        }
        for(int i=1;i<n;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1,0);
        for(int i=1;i<=m;++i){
            int l,r;
            scanf("%d%d",&l,&r);
            if(b[l]>b[r])
                swap(l,r);
            int LCA=lca(l,r);
            if(LCA==l) q[i]={b[l],b[r],l,r,LCA,i};
            else    q[i]={e[l],b[r],l,r,LCA,i};
        }
        sort(q+1,q+m+1);
        int l=1,r=0;
        for(int i=1;i<=m;++i){
            int ql=q[i].l,qr=q[i].r,x=q[i].x,y=q[i].y,LCA=q[i].LCA;
            while(l<ql) modify(id[l++]);
            while(l>ql) modify(id[--l]);
            while(r<qr) modify(id[++r]);
            while(r>qr) modify(id[r--]);
            int bj=0;
            if(!f[LCA]) modify(LCA),bj=1;
            ans[q[i].id]=now;
            if(bj) modify(LCA);
        }
        for(int i=1;i<=m;++i)
            printf("%d\n",ans[i]);
    }
    
    CAD加油!欢迎跟我一起讨论学习算法,QQ:1401650042
  • 相关阅读:
    Windows Server 2012 两台服务器文件同步
    Linux下非root用户运行Tomcat
    Linux离线安装mysql 5.6详细步骤
    spring再学习之整合JDBC
    spring再学习之AOP实操
    spring再学习之AOP准备
    spring再学习之注解
    spring再学习之配置详解
    spring再学习之基本概念
    spring再学习之简单测试
  • 原文地址:https://www.cnblogs.com/cader/p/14744600.html
Copyright © 2020-2023  润新知