首先没有连边的操作的时候,我们可以用可持久化线段树来维护这棵树的信息,建立权值可持久化线段树,那么每个点继承父节点的线段树,当询问为x,y的时候我们可以询问rot[x]+rot[y]-rot[lca(x,y)]-rot[lca(x,y)->father]这棵树来得知这个链的信息。

  那么对于连边操作,相当于合并两棵树,我们可以将树的节点数小的树全部拆掉连到节点大的树中,这样每个点最多会被操作logn次,每次操作的时间复杂度为logn,所以是mlog^2n的。

  反思:对于树的连通性我是用并查集维护的,对于合并操作还需要dfs一次小的树来维护各种信息,但是忘记对x,y点连边了,导致一直RE.(RE是因为某次值不正确,导致下一次^的点超过n)。

     各种慢= =。

/**************************************************************
Problem: 3123
User: BLADEVIL
Language: C++
Result: Accepted
Time:16612 ms
Memory:81512 kb
****************************************************************/ //By BLADEVIL
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100010
#define maxm 200010 using namespace std; struct rec {
int key,num;
rec() {
key=num=;
}
}a[maxn]; struct segment {
int left,right,cnt;
int son[];
segment() {
left=right=cnt=;
memset(son,,sizeof son);
}
}t[maxn<<]; int n,m,l,N,tot;
int pre[maxm<<],last[maxm<<],other[maxm<<];
int ans[maxn],rot[maxn];
int dep[maxn],jump[maxn][],father[maxn],size[maxn],que[maxn]; void connect(int x,int y) {
pre[++l]=last[x];
last[x]=l;
other[l]=y;
} int getfather(int x) {
if (father[x]==x) return x;
return father[x]=getfather(father[x]);
} void dfs(int x,int fa) {
jump[x][]=fa; dep[x]=dep[fa]+;
for (int p=last[x];p;p=pre[p]) {
if (other[p]==fa) continue;
dfs(other[p],x);
}
} bool cmp1(rec x,rec y) {
return x.key<y.key;
} bool cmp2(rec x,rec y) {
return x.num<y.num;
} int lca(int x,int y) {
if (dep[x]>dep[y]) swap(x,y);
int d=dep[y]-dep[x];
for (int i=;i<=;i++) if (d&(<<i)) y=jump[y][i];
if (x==y) return x;
for (int i=;i>=;i--) if (jump[x][i]!=jump[y][i]) x=jump[x][i],y=jump[y][i];
return jump[x][];
} void build(int &x,int l,int r) {
if (!x) x=++tot;
t[x].left=l; t[x].right=r;
if (t[x].left==t[x].right) return ;
int mid=t[x].left+t[x].right>>;
build(t[x].son[],l,mid); build(t[x].son[],mid+,r);
} void insert(int &x,int rot,int key) {
if (!x) x=++tot;
t[x].left=t[rot].left; t[x].right=t[rot].right;
if (t[x].left==t[x].right) {
t[x].cnt=t[rot].cnt+;
return ;
}
int mid=t[x].left+t[x].right>>;
if (key>mid) {
t[x].son[]=t[rot].son[];
insert(t[x].son[],t[rot].son[],key);
} else {
t[x].son[]=t[rot].son[];
insert(t[x].son[],t[rot].son[],key);
}
t[x].cnt=t[rot].cnt+;
} int query(int x,int y,int l1,int l2,int k) {
//printf("%d %d %d ",x,t[x].left,t[x].right);
if (t[x].left==t[x].right) return t[x].left;
int cur=t[t[x].son[]].cnt+t[t[y].son[]].cnt-t[t[l1].son[]].cnt-t[t[l2].son[]].cnt;
//printf("%d\n",cur);
if (k>cur) return query(t[x].son[],t[y].son[],t[l1].son[],t[l2].son[],k-cur); else
return query(t[x].son[],t[y].son[],t[l1].son[],t[l2].son[],k);
} void make(int x,int fa) {
insert(rot[x],rot[fa],a[x].key);
for (int p=last[x];p;p=pre[p]) {
if (other[p]==fa) continue;
make(other[p],x);
}
} void update(int x,int fa,int j) {
jump[x][j]=jump[jump[x][j-]][j-];
for (int p=last[x];p;p=pre[p]) {
if (other[p]==fa) continue;
update(other[p],x,j);
}
} void merge(int x,int y) {
int fx=getfather(x),fy=getfather(y);
//if (x==1) printf("%d %d\n",fx,fy);
if (size[fx]<size[fy]) swap(x,y);
fx=getfather(x),fy=getfather(y);
father[fy]=fx; size[fx]+=size[fy];
dfs(y,x); make(y,x);
for (int i=;i<=;i++) update(y,x,i);
//for (int i=1;i<=n;i++) printf("%d %d\n",i,jump[i][0]);
} int main() {
//freopen("3123.in","r",stdin); freopen("3123.out","w",stdout);
int task; scanf("%d",&task);
scanf("%d%d%d",&n,&m,&task);
for (int i=;i<=n;i++) scanf("%d",&a[a[i].num=i].key); sort(a+,a++n,cmp1);
int cur; ans[]=cur=a[].key;
for (int i=,j=;i<=n;i++)
if (a[i].key==cur) a[i].key=j; else ans[++j]=cur=a[i].key,a[i].key=j,N=j;
sort(a+,a++n,cmp2);
//for (int i=1;i<=n;i++) printf("%d ",ans[i]); printf("\n");
//for (int i=1;i<=n;i++) printf("%d ",a[i].key); printf("\n"); for (int i=;i<=n;i++) father[i]=i,size[i]=;
for (int i=;i<=m;i++) {
int x,y; scanf("%d%d",&x,&y);
connect(x,y); connect(y,x);
int fx=getfather(x),fy=getfather(y);
father[fx]=fy; size[fy]+=size[fx];
}
for (int i=;i<=n;i++) if (!jump[i][]) dfs(i,);
//for (int i=1;i<=n;i++) printf("%d %d\n",i,father[i]);
//for (int i=1;i<=n;i++) if (i==getfather(i)) printf("%d %d\n",i,size[i]); for (int j=;j<=;j++)
for (int i=;i<=n;i++) jump[i][j]=jump[jump[i][j-]][j-]; build(rot[],,N);
for (int i=;i<=n;i++) if (!jump[i][]) make(i,); int ANS=; char Q[];
while (task--) {
scanf("%s",Q);
if (Q[]=='Q') {
int x,y,k; scanf("%d%d%d",&x,&y,&k);
x^=ANS; y^=ANS; k^=ANS;
//printf("|%d %d %d\n",x,y,k);
int rr=lca(x,y); //printf("%d\n",rr);
//for (int i=1;i<=n;i++) printf("%d %d\n",i,jump[i][0]);
printf("%d\n",ANS=ans[query(rot[x],rot[y],rot[rr],rot[jump[rr][]],k)]);
//for (int i=1;i<=n;i++) if (i==getfather(i)) printf("%d %d\n",i,size[i]);
} else {
int x,y; scanf("%d%d",&x,&y);
x^=ANS; y^=ANS; connect(x,y); connect(y,x);
//printf("|%d %d\n",x,y);
merge(x,y);
}
}
return ;
}

 

bzoj 3123 可持久化线段树启发式合并的更多相关文章

  1. BZOJ - 3123 森林 (可持久化线段树+启发式合并)

    题目链接 先把初始边建成一个森林,每棵树选一个根节点递归建可持久化线段树.当添加新边的时候,把结点数少的树暴力重构,以和它连边的那个点作为父节点继承线段树,并求出倍增数组.树的结点数可以用并查集来维护 ...

  2. bzoj 3673&3674 可持久化并查集&加强版(可持久化线段树+启发式合并)

    CCZ在2015年8月25日也就是初三暑假要结束的时候就已经能切这种题了%%% 学习了另一种启发式合并的方法,按秩合并,也就是按树的深度合并,实际上是和按树的大小一个道理,但是感觉(至少在这题上)更好 ...

  3. Bzoj 3123: [Sdoi2013]森林(主席树+启发式合并)

    3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MB Description Input 第一行包含一个正整数testcase,表示当前 ...

  4. BZOJ 3123: [Sdoi2013]森林 [主席树启发式合并]

    3123: [Sdoi2013]森林 题意:一个森林,加边,询问路径上k小值.保证任意时刻是森林 LCT没法搞,树上kth肯定要用树上主席树 加边?启发式合并就好了,小的树dfs重建一下 注意 测试点 ...

  5. Bzoj 2733: [HNOI2012]永无乡(线段树+启发式合并)

    2733: [HNOI2012]永无乡 Time Limit: 10 Sec Memory Limit: 128 MB Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己 ...

  6. 【bzoj2653】middle 可持久化线段树区间合并

    题目描述 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整.给你一个长度为n的序列s.回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[ ...

  7. [CSP-S模拟测试]:模板(ac)(线段树启发式合并)

    题目描述 辣鸡$ljh\ NOI$之后就退役了,然后就滚去学文化课了.他每天都被$katarina$大神虐,仗着自己学过一些姿势就给$katarina$大神出了一道题.有一棵$n$个节点的以$1$号节 ...

  8. [ZJOI2019]语言(树链剖分+动态开点线段树+启发式合并)

    首先,对于从每个点出发的路径,答案一定是过这个点的路径所覆盖的点数.然后可以做树上差分,对每个点记录路径产生总贡献,然后做一个树剖维护,对每个点维护一个动态开点线段树.最后再从根节点开始做一遍dfs, ...

  9. bzoj 3207 可持久化线段树

    首先因为固定询问长度,所以我们可以将整个长度为n的数列hash成长度为n-k+1的数列,每次询问的序列也hash成一个数,然后询问这个数是不是在某个区间中出现过,这样我们可以根据初始数列的权值建立可持 ...

随机推荐

  1. node.js入门(二) 模块 事件驱动

    模块化结构 node.js 使用了 CommonJS 定义的模块系统.不同的功能组件被划分成不同的模块.应用可以根据自己的需要来选择使用合适的模块.每个模块都会暴露一些公共的方法或属性.模块使用者直接 ...

  2. IE8 没有内容的盒子,如果有定位,浮现在其他盒子上 可能会有点击穿透没有作用的情况

    IE8 没有内容的盒子,如果有定位,浮现在其他盒子上 可能会有点击穿透没有作用的情况

  3. iOS pch文件的创建

    3.iso pch头文件的创建  输入文件名的时候记得打钩 3.1.在Build Settings 里搜索pref就能找到preflx, 点击设置相对路径 $(SRCROOT) +路径:成功了就会显示 ...

  4. bzoj2788-Festival

    题意 有 \(n\) 个变量,有两种限制,分别有 \(m_1,m_2\) 种.限制如下: \(a_x+1=a_y\) \(a_x\le a_y\) 求 \(\{x_i\}\) 集合的大小.\(n\le ...

  5. java执行cmd命令并获取输出结果

    1.java执行cmd命令并获取输出结果 import java.io.BufferedReader; import java.io.InputStreamReader; import org.apa ...

  6. "流量监管"和"流量整形"的区别

    "流量监管" (Traffic Policing) 就是对流量进行控制,通过监督进入交换机端口的流量速率,对超出部分的流量进行"惩罚" (采用监管方式时是直接丢 ...

  7. 微软.NET Framework cve-2017-8759 复现

    0x00 漏洞前言 FireEye公司最近发现一份恶意微软Office RTF文档,其中利用到一项SOAP WSDL解析器代码注入漏洞——编号CVE-2017-8759.该漏洞允许恶意人士在解析SOA ...

  8. Sqli-labs之sql注入基础知识

    (1)注入的分类 基于从服务器接收到的响应  ▲基于错误的SQL注入 ▲联合查询的类型 ▲堆查询注射 ▲SQL盲注 •基于布尔SQL盲注 •基于时间的SQL盲注 •基于报错的SQL盲注 基于如何处理输 ...

  9. 并发时-修改Linux系统下的最大文件描述符限制

    通常我们通过终端连接到linux系统后执行ulimit -n 命令可以看到本次登录的session其文件描述符的限制,如下: $ulimit -n1024 当然可以通过ulimit -SHn 1024 ...

  10. Linux之Makefile20160707

    说一下LINUX下的Makefile,直接根据实际碰到的Makefile进行解读: 当make的目标为all时,-C $(KDIR) 指明跳转到内核源码目录下读取那里的Makefile:M=$(PWD ...