题目链接

第一种方法,dfs序上建可持久化线段树,然后询问的时候把两点之间的所有树链扒出来做差。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+,inf=0x3f3f3f3f;
int hd[N],ne,n,n2,m,fa[N],son[N],siz[N],dep[N],top[N],dfn[N],rnk[N],tot,rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N],ql[],qr[],nl,nr;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs1(int u,int f,int d) {
fa[u]=f,son[u]=,siz[u]=,dep[u]=d;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs1(v,u,d+),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int tp) {
top[u]=tp,dfn[u]=++tot,rnk[tot]=u;
if(son[u])dfs2(son[u],tp);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
#define mid ((l+r)>>1)
void upd(int& u,int v,int x,int l=,int r=n2) {
if(!u)u=++tot2;
val[u]=val[v]+;
if(l==r)return;
if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
}
int ask(int u,int v,int k) {
for(nl=nr=; top[u]!=top[v]; u=fa[top[u]]) {
if(dep[top[u]]<dep[top[v]])swap(u,v);
ql[nl++]=rt[dfn[top[u]]-],qr[nr++]=rt[dfn[u]];
}
if(dep[u]<dep[v])swap(u,v);
ql[nl++]=rt[dfn[v]-],qr[nr++]=rt[dfn[u]];
int l=,r=n2;
while(l<r) {
int cnt=;
for(int i=; i<nr; ++i)cnt+=val[ls[qr[i]]];
for(int i=; i<nl; ++i)cnt-=val[ls[ql[i]]];
if(k<=cnt) {
for(int i=; i<nr; ++i)qr[i]=ls[qr[i]];
for(int i=; i<nl; ++i)ql[i]=ls[ql[i]];
r=mid;
} else {
k-=cnt;
for(int i=; i<nr; ++i)qr[i]=rs[qr[i]];
for(int i=; i<nl; ++i)ql[i]=rs[ql[i]];
l=mid+;
}
}
return l;
}
int main() {
memset(hd,-,sizeof hd),ne=;
scanf("%d%d",&n,&m);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i-]=a[i];
sort(b,b+n),n2=unique(b,b+n)-b;
for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
tot=,dfs1(,,),dfs2(,);
memset(rt,,sizeof rt),tot2=;
for(int i=; i<=n; ++i)upd(rt[i],rt[i-],a[rnk[i]],,n2);
for(int last=; m--;) {
int u,v,k;
scanf("%d%d%d",&u,&v,&k),u^=last;
int ans=b[ask(u,v,k)-];
printf("%d\n",ans),last=ans;
}
return ;
}

仔细一想这样似乎麻烦了点。因为没有修改操作,我们可以直接用子结点继承父节点的方式来建线段树,然后查询的时候,用u,v的线段树减去lca的线段树再减去lca父节点的线段树即可。

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+,inf=0x3f3f3f3f;
int hd[N],ne,n,n2,m,fa[N],son[N],siz[N],dep[N],top[N],dfn[N],rnk[N],tot,rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N],ql[],qr[],nl,nr;
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
void dfs1(int u,int f,int d) {
fa[u]=f,son[u]=,siz[u]=,dep[u]=d;
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs1(v,u,d+),siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
void dfs2(int u,int tp) {
top[u]=tp,dfn[u]=++tot,rnk[tot]=u;
if(son[u])dfs2(son[u],tp);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
#define mid ((l+r)>>1)
void upd(int& u,int v,int x,int l=,int r=n2) {
if(!u)u=++tot2;
val[u]=val[v]+;
if(l==r)return;
if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
}
void dfs3(int u) {
upd(rt[u],rt[fa[u]],a[u]);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs3(v);
}
}
int lca(int u,int v) {
for(; top[u]!=top[v]; u=fa[top[u]])if(dep[top[u]]<dep[top[v]])swap(u,v);
return dep[u]<dep[v]?u:v;
}
int ask(int u,int v,int w1,int w2,int k,int l=,int r=n2) {
if(l==r)return l;
int cnt=val[ls[u]]+val[ls[v]]-val[ls[w1]]-val[ls[w2]];
return k<=cnt?ask(ls[u],ls[v],ls[w1],ls[w2],k,l,mid):ask(rs[u],rs[v],rs[w1],rs[w2],k-cnt,mid+,r);
}
int main() {
memset(hd,-,sizeof hd),ne=;
scanf("%d%d",&n,&m);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i-]=a[i];
sort(b,b+n),n2=unique(b,b+n)-b;
for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
tot=,dfs1(,,),dfs2(,);
memset(rt,,sizeof rt),tot2=;
dfs3();
for(int last=; m--;) {
int u,v,k;
scanf("%d%d%d",&u,&v,&k),u^=last;
int w=lca(u,v);
int ans=b[ask(rt[u],rt[v],rt[w],rt[fa[w]],k)-];
printf("%d\n",ans),last=ans;
}
return ;
}

然后我又测试了倍增和RMQ求LCA的方法,发现居然还不如dfs序+可持久化线段树的方法快~~毕竟倍增和RMQ预处理的时间和空间复杂度都是$O(nlogn)$,而树剖只需要$O(n)$,而且查询速度也比较快。

倍增:

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+,inf=0x3f3f3f3f;
int hd[N],ne,n,n2,m,fa[N][],dep[N],rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N];
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
#define mid ((l+r)>>1)
void upd(int& u,int v,int x,int l=,int r=n2) {
if(!u)u=++tot2;
val[u]=val[v]+;
if(l==r)return;
if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
}
void dfs(int u,int f,int d) {
fa[u][]=f,dep[u]=d,upd(rt[u],rt[fa[u][]],a[u]);
for(int i=; i<; ++i)fa[u][i]=fa[fa[u][i-]][i-];
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u][])continue;
dfs(v,u,d+);
}
}
int lca(int u,int v) {
if(dep[u]<dep[v])swap(u,v);
for(int i=; dep[u]!=dep[v]; --i)if(dep[fa[u][i]]>=dep[v])u=fa[u][i];
if(u==v)return u;
for(int i=; i>=; --i)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
return fa[u][];
}
int ask(int u,int v,int w1,int w2,int k,int l=,int r=n2) {
if(l==r)return l;
int cnt=val[ls[u]]+val[ls[v]]-val[ls[w1]]-val[ls[w2]];
return k<=cnt?ask(ls[u],ls[v],ls[w1],ls[w2],k,l,mid):ask(rs[u],rs[v],rs[w1],rs[w2],k-cnt,mid+,r);
}
int main() {
memset(hd,-,sizeof hd),ne=;
scanf("%d%d",&n,&m);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i-]=a[i];
sort(b,b+n),n2=unique(b,b+n)-b;
for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
memset(rt,,sizeof rt),tot2=;
dfs(,,);
for(int last=; m--;) {
int u,v,k;
scanf("%d%d%d",&u,&v,&k),u^=last;
int w=lca(u,v);
int ans=b[ask(rt[u],rt[v],rt[w],rt[fa[w][]],k)-];
printf("%d\n",ans),last=ans;
}
return ;
}

RMQ:

 #include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+,inf=0x3f3f3f3f;
int hd[N],ne,n,n2,m,fa[N],dep[N],pos[N],ST[N<<][],Log[N<<],tot,rt[N],ls[N*],rs[N*],val[N*],tot2,a[N],b[N];
struct E {int v,nxt;} e[N<<];
void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
#define mid ((l+r)>>1)
void upd(int& u,int v,int x,int l=,int r=n2) {
if(!u)u=++tot2;
val[u]=val[v]+;
if(l==r)return;
if(x<=mid)upd(ls[u],ls[v],x,l,mid),rs[u]=rs[v];
else upd(rs[u],rs[v],x,mid+,r),ls[u]=ls[v];
}
void dfs(int u,int f,int d) {
fa[u]=f,dep[u]=d,ST[++tot][]=u,pos[u]=tot,upd(rt[u],rt[fa[u]],a[u]);
for(int i=hd[u]; ~i; i=e[i].nxt) {
int v=e[i].v;
if(v==fa[u])continue;
dfs(v,u,d+),ST[++tot][]=u;
}
}
bool cmp(int a,int b) {return dep[a]<dep[b];}
void initST() {
for(int j=; j<; ++j)
for(int i=; i+(<<j)-<=tot; ++i)
ST[i][j]=min(ST[i][j-],ST[i+(<<(j-))][j-],cmp);
}
int lca(int u,int v) {
int l=pos[u],r=pos[v];
if(l>r)swap(l,r);
int i=Log[r-l+];
return min(ST[l][i],ST[r-(<<i)+][i],cmp);
}
int ask(int u,int v,int w1,int w2,int k,int l=,int r=n2) {
if(l==r)return l;
int cnt=val[ls[u]]+val[ls[v]]-val[ls[w1]]-val[ls[w2]];
return k<=cnt?ask(ls[u],ls[v],ls[w1],ls[w2],k,l,mid):ask(rs[u],rs[v],rs[w1],rs[w2],k-cnt,mid+,r);
}
int main() {
for(int i=; i<(N<<); ++i)Log[i]=log2(i+0.5);
memset(hd,-,sizeof hd),ne=;
scanf("%d%d",&n,&m);
for(int i=; i<=n; ++i)scanf("%d",&a[i]);
for(int i=; i<=n; ++i)b[i-]=a[i];
sort(b,b+n),n2=unique(b,b+n)-b;
for(int i=; i<=n; ++i)a[i]=(lower_bound(b,b+n2,a[i])-b)+;
for(int i=; i<n; ++i) {
int u,v;
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
memset(rt,,sizeof rt),tot2=;
dfs(,,),initST();
for(int last=; m--;) {
int u,v,k;
scanf("%d%d%d",&u,&v,&k),u^=last;
int w=lca(u,v);
int ans=b[ask(rt[u],rt[v],rt[w],rt[fa[w]],k)-];
printf("%d\n",ans),last=ans;
}
return ;
}

BZOJ - 2588 Spoj 10628. Count on a tree (可持久化线段树+LCA/树链剖分)的更多相关文章

  1. BZOJ 2588: Spoj 10628. Count on a tree [树上主席树]

    2588: Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 5217  Solved: 1233 ...

  2. BZOJ 2588: Spoj 10628. Count on a tree 树上跑主席树

    2588: Spoj 10628. Count on a tree Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://www.lydsy.com/J ...

  3. Bzoj 2588: Spoj 10628. Count on a tree 主席树,离散化,可持久,倍增LCA

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2588 2588: Spoj 10628. Count on a tree Time Limit ...

  4. BZOJ 2588: Spoj 10628. Count on a tree( LCA + 主席树 )

    Orz..跑得还挺快的#10 自从会树链剖分后LCA就没写过倍增了... 这道题用可持久化线段树..点x的线段树表示ROOT到x的这条路径上的权值线段树 ----------------------- ...

  5. Bzoj 2588 Spoj 10628. Count on a tree(树链剖分LCA+主席树)

    2588: Spoj 10628. Count on a tree Time Limit: 12 Sec Memory Limit: 128 MB Description 给定一棵N个节点的树,每个点 ...

  6. bzoj 2588 Spoj 10628. Count on a tree (可持久化线段树)

    Spoj 10628. Count on a tree Time Limit: 12 Sec  Memory Limit: 128 MBSubmit: 7669  Solved: 1894[Submi ...

  7. 主席树 || 可持久化线段树 || LCA || BZOJ 2588: Spoj 10628. Count on a tree || Luogu P2633 Count on a tree

    题面: Count on a tree 题解: 主席树维护每个节点到根节点的权值出现次数,大体和主席树典型做法差不多,对于询问(X,Y),答案要计算ans(X)+ans(Y)-ans(LCA(X,Y) ...

  8. BZOJ 2588: Spoj 10628. Count on a tree 主席树+lca

    分析:树上第k小,然后我想说的是主席树并不局限于线性表 详细分析请看http://www.cnblogs.com/rausen/p/4006116.html,讲的很好, 然后因为这个熟悉了主席树,真是 ...

  9. ●BZOJ 2588 Spoj 10628. Count on a tree

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2588 题解: 主席树,在线,(求LCA)感觉主席树真的好厉害...在原树上建主席树.即对于原 ...

随机推荐

  1. 【转】Python爬虫(1)_基本原理

    一 爬虫是什么 #如果我们把互联网比作一张大的蜘蛛网,数据便是存放于蜘蛛网的各个节点,而爬虫就是一只小蜘蛛,沿着网络抓取自己的猎物/数据 #爬虫指的是:向网站发起请求,获取资源后分析并提取有用数据的程 ...

  2. 谷歌机器学习速成课程---3降低损失 (Reducing Loss):梯度下降法

    迭代方法图(图 1)包含一个标题为“计算参数更新”的华而不实的绿框.现在,我们将用更实质的方法代替这种华而不实的算法. 假设我们有时间和计算资源来计算 w1 的所有可能值的损失.对于我们一直在研究的回 ...

  3. FTP下载

    import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import ja ...

  4. 【Head First Servlets and JSP】迷你MVC:JarDownload的完整实现

    1.首先,写一个download.html放至D:\apache-tomcat-7.0.77\webapps\JarDownload-v1. <!DOCTYPE HTML> <htm ...

  5. Mysql 主从复制搭建

    Mysql 主重复制搭建 Linux版本:Linux Centos 6.4 32位 Mysql版本:Mysql-5.6.38-linux-glibc2.12-i686 Mysql安装:Mysql安装教 ...

  6. 在shell,R,python中用变量和常量创建文件名

    很多时候我们希望文件名的格式是:变量+常量的. 1.shell:变量"常量" [wangjq@mgmt multi_pcr]$ a="var" [wangjq@ ...

  7. 恢复性训练day1

    DP: 0/1背包一个常见的错误是没有cmax(f[i][j],f[i-1][j]) 0/1背包的拓展中有转移式的变形,以及无限数量背包,分组背包等. 可化为背包问题的一般不会太难. 数组开小,出现大 ...

  8. 对正交频分复用OFDM系统的理解

    OFDM系统 正交频分复用OFDM(Orthogonal Frenquency Division Multiplexing)是一种多载波调制技术. 基本思想:在发送端,它将高速串行数据经过串并变换形成 ...

  9. URI Is Not Registered

    使用IntelliJ Maven生成archetype时候,偶然会出现xml文件的头定义提示错误 URI is not registered 例如: 解决方法: 鼠标点击红色字,然后Intellij出 ...

  10. jprofile查看hprof文件[转]

    用jprofile打开hprof文件,查看内存泄露情况,有几个常用的功能说明一下: 工具下载:到官网下载jprofile7.0.1 64位的.再申请一个注册号,注册号的申请好像是一个邮件只能用一次. ...