主席树+启发式合并(LT) BZOJ3123

好久没做题了,写道SBT又RE又T

查询:主席树裸题。

修改:对于两个树合并重建小的树。

注意fa[x][i]重新计算时要清空

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
    char c=getchar();int x=0,sig=1;
    for(;!isdigit(c);c=getchar()) if(c==‘-‘) sig=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘;
    return x*sig;
}
inline void print(int x)
{
    int buf[10],l=0;
    if(!x) l++;
    while(x) buf[++l]=x%10,x/=10;
    for(;l;l--) putchar(buf[l]+‘0‘);
    putchar(‘\n‘);
}
const int maxn=80010;
const int maxnode=10000010;
int ls[maxnode],rs[maxnode],s[maxnode],ToT;
void update(int& y,int x,int l,int r,int pos)
{
     s[y=++ToT]=s[x]+1;if(l==r) return;
     int mid=l+r>>1;ls[y]=ls[x];rs[y]=rs[x];
     if(pos<=mid) update(ls[y],ls[x],l,mid,pos);
     else update(rs[y],rs[x],mid+1,r,pos);
}
int query(int x1,int x2,int y1,int y2,int l,int r,int k)
{
    if(l==r) return l;
    int mid=l+r>>1,k2=s[ls[y1]]+s[ls[y2]]-s[ls[x1]]-s[ls[x2]];
    if(k<=k2) return query(ls[x1],ls[x2],ls[y1],ls[y2],l,mid,k);
    return query(rs[x1],rs[x2],rs[y1],rs[y2],mid+1,r,k-k2);
}
int n,m,q,val[maxn],tmp[maxn],root[maxn],lastans;
int first[maxn],next[maxn*2],to[maxn*2],e,fa[maxn][20],dep[maxn];
int pa[maxn],sz[maxn];
void AddEdge(int a,int b)
{
    to[++e]=b;next[e]=first[a];first[a]=e;
    to[++e]=a;next[e]=first[b];first[b]=e;
}
void dfs(int x,int f)
{
    dep[x]=dep[fa[x][0]=f]+1;update(root[x],root[f],1,n,val[x]);
    sz[pa[x]]++;
    for(int i=1;i<17;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    for(int i=first[x];i;i=next[i]) if(to[i]!=f) pa[to[i]]=pa[x],dfs(to[i],x);
}
int lca(int p,int q)
{
    if(dep[p]<dep[q]) swap(p,q);
    for(int i=19;i>=0;i--) if((1<<i)<=dep[p]-dep[q]) p=fa[p][i];
    for(int i=19;i>=0;i--) if(fa[p][i]!=fa[q][i]) p=fa[p][i],q=fa[q][i];
    return p==q?p:fa[p][0];
}
int main()
{
    read();n=read();m=read();q=read();
    for(int i=1;i<=n;i++) tmp[i]=val[i]=read();
    sort(tmp+1,tmp+n+1);
    for(int i=1;i<=n;i++) val[i]=lower_bound(tmp+1,tmp+n+1,val[i])-tmp;
    for(int i=0;i<m;i++) AddEdge(read(),read());
    for(int i=1;i<=n;i++) if(!fa[i][0]) pa[i]=i,dfs(i,0);
    for(int i=0;i<q;i++)
    {
        char tp=getchar();
        while(!isalpha(tp)) tp=getchar();
        if(tp==‘Q‘)
        {
            int x=read()^lastans,y=read()^lastans,k=read()^lastans;
            int c=lca(x,y);
            print(lastans=tmp[query(root[c],root[fa[c][0]],root[x],root[y],1,n,k)]);
        }
        else
        {
            int u=read()^lastans,v=read()^lastans;
            int x=pa[u],y=pa[v];
            if(sz[x]>sz[y]) swap(x,y),swap(u,v);
            pa[u]=y;AddEdge(u,v);dfs(u,v);
        }
    }
    return 0;
}

时间: 2024-08-29 17:20:07

主席树+启发式合并(LT) BZOJ3123的相关文章

【主席树 启发式合并】bzoj3123: [Sdoi2013]森林

小细节磕磕碰碰浪费了半个多小时的时间 Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值.  接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为“Q x y k”或者“L x y ”,其含义见题目描述部分. Output 对于每

[bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数.第三行包含N个非负整数表示 N个节点上的权值. 接下来 M行,每行包含两个整数x和 y,表示初始的时候,点x和点y 之间有一条无向边, 接下来 T行,每行描述一个操作,格式为"Q x y k"或者"L x y ",其含义见题目描述部分. Output 对于每一个第一类

【BZOJ 3123】 [Sdoi2013]森林 主席树启发式合并

我们直接按父子关系建主席树,然后记录倍增方便以后求LCA,同时用并查集维护根节点,而且还要记录根节点对应的size,用来对其启发式合并,然后每当我们合并的时候我们都要暴力拆小的一部分重复以上部分,总时间复杂度为O(n*log),因为每个的节点只会作为小的部分合并,因此他所在的一小部分至少变大2倍,对于每一个节点他作为被合并方最多log次,因此其复杂度为O(n*log),而这个是绝对跑不满还差很多的,我们视他为无常数就好了,当然这一切都是建立在无拆分操作的基础之上,只要有了拆分启发式合并的复杂度就

【主席树启发式合并】【P3302】[SDOI2013]森林

Description 给定一个 \(n\) 个节点的森林,有 \(Q\) 次操作,每次要么将森林中某两点联通,保证操作后还是个森林,要么查询两点间权值第 \(k\) 小,保证两点联通.强制在线. Limitation \(1~\leq~n,~Q~\leq~80000\) Solution 考虑有连边还有查询链上第 \(k\) 大,于是要么用 LCT,要么用主席树. 考虑如果用 LCT 的话,并不能快速的维护两点间链的信息(其实感觉在access的时候乱搞一下有希望在多一个 \(\log\) 的

p3302 [SDOI2013]森林(树上主席树+启发式合并)

对着题目yy了一天加上看了一中午题解,终于搞明白了我太弱了 连边就是合并线段树,把小的集合合并到大的上,可以保证规模至少增加一半,复杂度可以是\(O(logn)\) 合并的时候暴力dfs修改倍增数组和维护主席树即可 然后树上主席树就是维护节点到根节点的信息即可, 询问链上的第k大时,画图后可以发现维护一个rootx,rooty,rootlca和rootfalca即可解决问题 注意空间要开够 然后注意细节,没了 #include <cstdio> #include <cstring>

bzoj3123: [Sdoi2013]森林 主席树+启发式合并

思博题加强版,还是思博题,RT,没了. 内存log^2,写了回收的话可以少个log. lca不能用树剖了好悲伤(IoI),讨厌倍增. 没有1A好不爽啊啊啊,最近写思博题只有一道1A的是要退役的节奏(@[email protected]) #include<cstdio> #include<algorithm> #define N 80005 #define M (l+r>>1) using namespace std; char o[2]; int k,m,q,s,t,

BZOJ3123LCA+主席树+启发式合并

d穆善蓟8悼z旱移7http://www.zcool.com.cn/collection/ZMTg1MjE5MDg=.html 自9B是O焕n偷儆裙1http://www.zcool.com.cn/collection/ZMTg1MjE5NTY=.html 088酥1FVL斗稳7http://www.zcool.com.cn/collection/ZMTg1MjIwNjQ=.html 271U59mYG寂灾http://www.zcool.com.cn/collection/ZMTg1MjIxM

BZOJ 3123 SDOI 2013 森林 可持久化线段树+启发式合并

题目大意:给出一个森林,每个节点都有一个权值.有若干加边操作,问两点之间路径上的第k小权值是多少. 思路:这题和COT1比较像,但是多了连接操作.这样就只能暴力合并连个树.启发式合并会保证时间复杂度不至于太大.然后就是用可持久化线段树维护一个树的信息,按照dfs序来建树,每个节点的可持久化链的参考版本就是它父亲的版本.之后利用权值线段树可区间加减的特性,用f[x] + f[y] - f[lca] - f[father[lca]]来计算权值. CODE: #include <cstdio> #i

HDU6191 Query on A Tree (01字典树+启发式合并)

题意: 给你一棵1e5的有根树,每个节点有点权,1e5个询问(u,x),问你子树u中与x异或最大的值是多少 思路: 自下而上启发式合并01字典树,注意合并时清空trie 线段树.字典树这种结构确定的数据结构,启发式合并的时候不需要考虑次序,复杂度都是nlogn 代码: 2200 / 10000ms , 60 / 128 M #include<iostream> #include<cstdio> #include<algorithm> #include<cmath&