zoukankan      html  css  js  c++  java
  • SPOJ COT2 (树上莫队)

    传送门

    题意:

    给你一棵大小为$n$的树,每个点都有点权。现在有$m$个询问,每个询问给你一个两个数$a,b$,问你从点$a$到点$b$之间的路径中不同的点权的个数。

    分析:

    万恶的spoj并没有写点权的数据范围,害我我先re(此题需要离散化点权)

    求解带有询问的不同数的个数这类题,~~一看就相当莫队~~ ,但是因为莫队只能够在一个序列上进行操作,因此我们考虑如何让树的树形结构转化为线性的结构。

    我们考虑使用树的欧拉序。

    我们设$st[]$为第一次遍历经过的编号,$en[]$为回溯时经过的编号,则现在有两种方案:

    1. 如果两个节点$a,b$在同一条链上,则我们直接选取欧拉序区间$([st[a],st[b])$
    2. 如果两个结点不在同一条链上,则我们直接选取欧拉序区间$(en[a],st[b])$

    对于每个区间,我们只需要统计该区间中,数字只出现一次的数字的个数。

    与此同时,因为在操作$2$中,两个节点的最近公共祖先是没有被贡献的,因此我们还得加上$LCA(a,b)$的贡献。

    之后就是我们普通莫队的分块了。

    #include <bits/stdc++.h>
    #define maxn 200005
    #define K 20
    using namespace std;
    struct P{
        int l,r,z,id;
    }Q[maxn];
    struct Node{
        int to,next;
    }q[maxn<<1];
    int lim,Cnt,pos[maxn<<1],c[maxn];
    int n,m,loc[maxn<<1],dfn,st[maxn],en[maxn],d[maxn],f[maxn][25];
    int ans[maxn],cnt[maxn],sum,head[maxn];
    bool vis[maxn];
    bool cmp(const P&a,const P&b){
        return pos[a.l]==pos[b.l]?a.r<b.r:pos[a.l]<pos[b.l];
    }
    void add_edge(int from,int to){
        q[Cnt].to=to;
        q[Cnt].next=head[from];
        head[from]=Cnt++;
    }
    void dfs(int x){
        loc[st[x]=++dfn]=x;
        vis[x]=1;
        for(int i=1;i<=K;i++) f[x][i]=f[f[x][i-1]][i-1];
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(!vis[to]) d[to]=d[f[to][0]=x]+1,dfs(to);
        }
        loc[en[x]=++dfn]=x;
    }
    int lca(int x,int y){
        if(x==y)return x;
        if(d[x]<d[y])swap(x,y);
        for(int i=K;~i;i--) if(d[f[x][i]]>=d[y])x=f[x][i];
        if(x==y) return x;
        for(int i=K;~i;i--){
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        }
        return f[x][0];
    }
    void deal(int x){
        if(!vis[x]){
            if(!(--cnt[c[x]])) sum--;
        }
        else if(!(cnt[c[x]]++)) sum++;
        vis[x]^=1;
    }
    int D[maxn];
    int main()
    {
        scanf("%d%d",&n,&m);
        memset(head,-1,sizeof(head));
        Cnt=0;
        for(int i=1;i<=n;i++){
            scanf("%d",&c[i]);
            D[i]=c[i];
        }
        sort(D+1,D+1+n);
        for(int i=1;i<=n;i++) c[i]=lower_bound(D+1,D+1+n,c[i])-D;
        for(int i=1;i<n;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            add_edge(x,y),add_edge(y,x);
        }
        dfs(d[1]=1),lim=(int)sqrt(n*2+0.5);
        for(int i=1;i<=dfn;i++) pos[i]=(i-1)/lim+1;
        for(int i=1;i<=m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            Q[i].id=i;
            if(st[x]>st[y]) swap(x,y);
            int z=lca(x,y);
            if(z==x) Q[i].l=st[x],Q[i].r=st[y];
            else Q[i].l=en[x],Q[i].r=st[y],Q[i].z=z;
        }
        sort(Q+1,Q+1+m,cmp);
        int l=1,r=0;
        for(int i=1;i<=m;i++){
            while(r<Q[i].r) deal(loc[++r]);
            while(r>Q[i].r) deal(loc[r--]);
            while(l<Q[i].l) deal(loc[l++]);
            while(l>Q[i].l) deal(loc[--l]);
            if(Q[i].z) deal(Q[i].z);
            ans[Q[i].id]=sum;
            if(Q[i].z) deal(Q[i].z);
        }
        for(int i=1;i<=m;i++) printf("%d
    ",ans[i]);
    }
    
  • 相关阅读:
    搭建woocomerce网站
    Cozmo 机器人编程环境搭建
    DevExpress Wizard的控件使用方法
    DevExpress 地图的控件使用方法
    DevExpress 摄像机的控件使用方法
    大疆第一人称视角眼镜goggle激活
    iis支持asp.net4.0的注册命令使用方法
    【转】PowerDesigner删除外键关系,而不删除外键列
    【转】ABP webapi三种方式
    【转】OAuth2.0的refresh token
  • 原文地址:https://www.cnblogs.com/Chen-Jr/p/11007138.html
Copyright © 2011-2022 走看看