题目描述

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v),你需要回答u xor lastans和v这两个节点间有多少种不同的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

输入格式

第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v),表示一组询问。
数据范围是N<=40000 M<=100000 点权在int范围内 

输出格式

M行,表示每个询问的答案。

  • 题解

    • 树上莫队;
    • 如果不要求强制在线的话比较传统;
    • 强制在线有点麻烦:
    • 对树按深度分块,当一个点向下的深度超过$\sqrt{N}$就分一块;
    • 这样分块保证了深度和块的数量在$O(\sqrt{N})$内,并且每一个块都是一颗子树;
    • 预处理每个块的根对其他所有点的答案,这是$O(n\sqrt{N})$的时间和空间的;
    • 考虑一个询问$u,v$如果在同一个块里则暴力查询;$O(\sqrt{N})$
    • 否则假设$u$的根比$v$的根要深,否则交换;
    • 利用预处理得到$(root_{u},v)$的答案$O(1)$,暴力查询$u$到$root_{u})$的颜色$O(\sqrt{N})$;
    • 只需要查询某个颜色是否出现过;
    • 可持久化块状链表记录$u$到原树的根链上的每个颜色出现的最大深度;$O(\sqrt{N})$
    • 枚举的颜色没有出现过即查询颜色的最大深度一定小于$lca$的深度;
      • 可持久化块状链表:
      • 对颜色分块,记录每个历史版本每个块的起点;
      • 插入的时候暴力复制上一个版本的起点,暴力新建一个修改位置的块并更新起点
  •  #include<bits/stdc++.h>
    using namespace std;
    const int N=,M=;
    int n,m,o=,hd[N],F[N],num[N],tot,sub[N],dep[N],rt,c[N],ans;
    int B,sum[M][N],bl[N],cnt,len[N],Rt[M],sta[N],top;
    int min(int x,int y){return x<y?x:y;}
    int max(int x,int y){return x>y?x:y;}
    struct Edge{int v,nt;}E[N<<];
    char gc(){
    static char*p1,*p2,s[];
    if(p1==p2)p2=(p1=s)+fread(s,,,stdin);
    return(p1==p2)?EOF:*p1++;
    }
    int rd(){
    int x=,f=; char c=gc();
    while(c<''||c>''){if(c=='-')f=-;c=gc();}
    while(c>=''&&c<='')x=(x<<)+(x<<)+c-'',c=gc();
    return x*f;
    }
    void adde(int u,int v){
    E[o]=(Edge){v,hd[u]};hd[u]=o++;
    E[o]=(Edge){u,hd[v]};hd[v]=o++;
    }
    namespace Block{
    int l[N][M],a[N*M],Cnt,bl[N],B,st[M],ed[M],sz;
    void init(){
    B=sqrt(tot)+;
    for(int i=;i<=tot;++i)bl[i]=(i-)/B+,a[i]=-;
    sz=bl[tot];
    for(int i=;i<=sz;++i){
    l[][i]=st[i]=ed[i-]+;
    ed[i]=ed[i-]+B;
    }
    ed[sz]=Cnt=tot;
    }
    int que(int u,int x){return a[l[u][bl[x]]+(x-)%B];}
    void ins(int u,int x){
    for(int i=;i<=sz;++i)l[u][i]=l[F[u]][i];
    int len=ed[bl[x]]-st[bl[x]],tmp=l[u][bl[x]],pre=Cnt;
    for(int i=;i<=len;++i)a[++Cnt]=a[tmp+i];
    a[pre++(x-)%B]=dep[u];
    l[u][bl[x]]=pre+;
    }
    }
    void upd(int x,int y){
    if((num[x]==)^(num[x]+y==))tot+=y;
    num[x]+=y;
    }
    void dfs1(int u,int fa){
    upd(c[u],);
    sum[cnt][u]=tot;
    for(int i=hd[u];i;i=E[i].nt)if(E[i].v!=fa)dfs1(E[i].v,u);
    upd(c[u],-);
    }
    void dfs(int u,int fa){
    int x=c[u];
    F[u]=fa;
    Block::ins(u,x);
    sta[++top]=u;
    for(int i=hd[u];i;i=E[i].nt){
    int v=E[i].v;
    if(v==fa)continue;
    dep[v]=dep[u]+;
    dfs(v,u);
    len[u]=max(len[v]+,len[u]);
    }
    if(len[u]>=B||u==){
    len[u]=-;
    Rt[++cnt]=u;
    int v;do{
    bl[v=sta[top--]]=cnt;
    }while(v!=u);
    dfs1(u,);
    }
    }
    namespace LCA{
    int son[N],tp[N],sz[N],F[N];
    void dfs1(int u,int fa){
    sz[u]=;son[u]=;F[u]=fa;
    for(int i=hd[u];i;i=E[i].nt){
    int v=E[i].v;
    if(v==fa)continue;
    dfs1(v,u);
    sz[u]+=sz[v];
    if(sz[v]>sz[son[u]])son[u]=v;
    }
    }
    void dfs2(int u,int T){
    tp[u]=T;
    if(son[u])dfs2(son[u],T);
    for(int i=hd[u];i;i=E[i].nt){
    int v=E[i].v;
    if(v==F[u]||v==son[u])continue;
    dfs2(v,v);
    }
    }
    int ask(int u,int v){
    int tu=tp[u],tv=tp[v];
    while(tu!=tv){
    if(dep[tu]<dep[tv])v=F[tv],tv=tp[v];
    else u=F[tu],tu=tp[u];
    }
    return dep[u]<dep[v]?u:v;
    }
    void init(){dfs1(,);dfs2(,);}
    }
    void query1(int u,int v){
    int x=Rt[bl[u]],y=Rt[bl[v]],dz=dep[LCA::ask(u,v)];
    if(dep[x]<dep[y])swap(x,y),swap(u,v);
    ans=sum[bl[u]][v];
    for(int w=u;w!=x;w=F[w]){
    int d = max(Block::que(x,c[w]),Block::que(v,c[w]));
    if(!num[c[w]]&&d<dz)num[c[w]]=,ans++;
    }
    for(int w=u;w!=x;w=F[w])num[c[w]]=;
    printf("%d\n",ans);
    }
    void query2(int u,int v){
    int dz=dep[LCA::ask(u,v)];
    ans=;
    for(int w=u;dep[w]>=dz;w=F[w])if(!(num[c[w]]++))ans++;
    for(int w=v;dep[w]>=dz;w=F[w])if(!(num[c[w]]++))ans++;
    for(int w=u;dep[w]>=dz;w=F[w])num[c[w]]=;
    for(int w=v;dep[w]>=dz;w=F[w])num[c[w]]=;
    printf("%d\n",ans);
    }
    int find(int x){
    int l=,r=tot,mid;
    while(l<r){
    if(x<=sub[mid=l+r>>])r=mid;
    else l=mid+;
    }
    return l;
    }
    int main(){
    #ifndef ONLINE_JUDGE
    freopen("bzoj2589.in","r",stdin);
    freopen("bzoj2589.out","w",stdout);
    #endif
    n=rd();m=rd();
    B=sqrt(n)+;
    for(int i=,x;i<=n;++i)c[i]=sub[i]=rd();
    for(int i=,u,v;i<n;++i)adde(rd(),rd());
    tot=n;sort(sub+,sub+tot+);
    tot=unique(sub+,sub+tot+)-sub-;
    for(int i=;i<=n;++i)c[i]=lower_bound(sub+,sub+tot+,c[i])-sub;
    Block::init();
    dep[]=-;
    tot=;dfs(,);
    LCA::init();
    for(int i=,x,y;i<=m;++i){
    x=rd()^ans,y=rd();
    if(bl[x]!=bl[y])query1(x,y);
    else query2(x,y);
    }
    return ;
    }

    bzoj2589

bzoj2589【 Spoj 10707】 Count on a tree II的更多相关文章

  1. SPOJ 10707 COT2 - Count on a tree II

    思路 树上莫队的题目 每次更新(u1,u2)和(v1,v2)(不包括lca)的路径,最后单独统计LCA即可 代码 #include <cstdio> #include <cstrin ...

  2. 【SPOJ】Count On A Tree II(树上莫队)

    [SPOJ]Count On A Tree II(树上莫队) 题面 洛谷 Vjudge 洛谷上有翻译啦 题解 如果不在树上就是一个很裸很裸的莫队 现在在树上,就是一个很裸很裸的树上莫队啦. #incl ...

  3. 主席树+LCA【p2633 (bzoj2588】 Count on a tree

    Description 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问的答案,初始 ...

  4. 【bzoj2588/P2633】count on a tree —— LCA + 主席树

    (以下是luogu题面) 题目描述 给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v,k),你需要回答u xor lastans和v这两个节点间第K小的点权.其中lastans是上一个询问 ...

  5. 【SPOJ QTREE4】Query on a tree IV(树链剖分)

    Description 给出一棵边带权(\(c\))的节点数量为 \(n\) 的树,初始树上所有节点都是白色.有两种操作: C x,改变节点 \(x\) 的颜色,即白变黑,黑变白. A,询问树中最远的 ...

  6. SPOJ:COT2 Count on a tree II

    题意 给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数. n=40000,m=100000 Sol 树上莫队模板题 # include <bits/stdc++.h ...

  7. 【BZOJ2589】 Spoj 10707 Count on a tree II

    BZOJ2589 Spoj 10707 Count on a tree II Solution 吐槽:这道题目简直...丧心病狂 如果没有强制在线不就是树上莫队入门题? 如果加了强制在线怎么做? 考虑 ...

  8. 【BZOJ2589】[SPOJ10707]Count on a tree II

    [BZOJ2589][SPOJ10707]Count on a tree II 题面 bzoj 题解 这题如果不强制在线就是一个很\(sb\)的莫队了,但是它强制在线啊\(qaq\) 所以我们就用到了 ...

  9. 【 SPOJ - GRASSPLA】 Grass Planting (树链剖分+树状数组)

    54  种草约翰有 N 个牧场,编号为 1 到 N.它们之间有 N − 1 条道路,每条道路连接两个牧场.通过这些道路,所有牧场都是连通的.刚开始的时候,所有道路都是光秃秃的,没有青草.约翰会在一些道 ...

随机推荐

  1. 对React children 的深入理解

    React的核心为组件.你可以像嵌套HTML标签一样嵌套使用这些组件,这使得编写JSX更加容易因为它类似于标记语言. 当我刚开始学习React时,当时我认为“使用 props.children 就这么 ...

  2. mongodb基本使用(三)

    MongoDB 创建数据库 语法 MongoDB 创建数据库的语法格式如下: use DATABASE_NAME 如果数据库不存在,则创建数据库,否则切换到指定数据库. 如果你想查看所有数据库,可以使 ...

  3. 微信小程序——音阶练耳 宣传页面

    音阶练耳是什么? 音阶练耳小程序是一款听音练习音阶,拥有简介界面的交互式小程序,以虚拟钢琴为辅助乐器,应用于日常练习,涵盖了五个八度内26种调式.以及下行中的所有调式与和声小调式的衍生,提高辨认音阶的 ...

  4. Teamwork(The third day of the team)

    在确定了第一个spring后我们就开始了各自的工作,不过由于大家都在专注于自己的工作并且由于近段时间的作业及各方面的事情都很多,没有来得及每天都更新一个博客,因此,我们现在把落下的博客都补上,很多事情 ...

  5. 阅读<构建之法>第三10、11、12章并提出问题

    <构建之法>第10.11.12章 第10章: 问题:对我们了解了用户的需求后,但是我们想法和做出来的软件会和用户的需求有偏差,比如风格.界面的修饰等等,那么我们程序猿怎样才能让自己的想法更 ...

  6. 【流程图】购物车、三级菜单、sed替换

  7. Android表情开发

    Android表情开发 效果图:            源码下载-github:https://github.com/SiberiaDante/EmotionApp (觉得有用的给个星星,支持一下哦)

  8. HDU 2078 复习时间

    http://acm.hdu.edu.cn/showproblem.php?pid=2078 Problem Description 为了能过个好年,xhd开始复习了,于是每天晚上背着书往教室跑.xh ...

  9. 七类网线 支持10gb/s的速度的计算方法

    0. 说明 我是数学白痴, 只是套了下公式... 1. 七类网线的参数: 七类线 ISO/IEC 7类/F级标准 中最新的一种双绞线,它主要为了适应万兆位以太网技术的应用和发展.但它不再是一种非屏蔽双 ...

  10. Linux 压缩 解压缩 命令相关

    1.命令格式:tar[必要参数][选择参数][文件] 2.命令功能:用来压缩和解压文件.tar本身不具有压缩功能.他是调用压缩功能实现的 3.命令参数:必要参数有如下:-A 新增压缩文件到已存在的压缩 ...