BZOJ3123:[SDOI2013]森林——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=3123

https://www.luogu.org/problemnew/show/P3302

树上主席树操作方法看:http://www.cnblogs.com/luyouqi233/p/8159528.html

BZOJ2588:Count on a tree

这题要动态树,显然不可能LCT套主席树啊。

那我们完全可以启发式合并一下主席树。

剩下的操作就很简单了。

(然而我debug两个小时才发现我n定义了两个emmmm)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=8e4+10;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch==‘-‘;ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct tree{
    int l,r,sum;
}tr[N*500];
struct node{
    int to,nxt;
}edge[N*2];
int a[N],b[N],rt[N],pool,n,m;
int dep[N],anc[N][20],son[N];
int cnt,head[N],fa[N],vis[N],tot;
inline void add(int u,int v){
    edge[++cnt].to=v;edge[cnt].nxt=head[u];head[u]=cnt;
}
inline void insert(int y,int &x,int l,int r,int p){
    tr[x=++pool]=tr[y];tr[x].sum++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)insert(tr[y].l,tr[x].l,l,mid,p);
    else insert(tr[y].r,tr[x].r,mid+1,r,p);
}
inline int query(int nl,int nr,int nm,int nfm,int l,int r,int k){
    if(l==r)return l;
    int delta=tr[tr[nl].l].sum+tr[tr[nr].l].sum-tr[tr[nm].l].sum-tr[tr[nfm].l].sum;
    int mid=(l+r)>>1;
    if(delta>=k)return query(tr[nl].l,tr[nr].l,tr[nm].l,tr[nfm].l,l,mid,k);
    else return query(tr[nl].r,tr[nr].r,tr[nm].r,tr[nfm].r,mid+1,r,k-delta);
}
inline void LSH(){
    sort(b+1,b+m+1);
    m=unique(b+1,b+m+1)-b-1;
    for(int i=1;i<=n;i++){
    a[i]=lower_bound(b+1,b+m+1,a[i])-b;
    }
    return;
}
inline int LCA(int i,int j){
    if(dep[i]<dep[j])swap(i,j);
    for(int k=16;k>=0;k--){
        if(dep[anc[i][k]]>=dep[j])i=anc[i][k];
    }
    if(i==j)return i;
    for(int k=16;k>=0;k--){
        if(anc[i][k]!=anc[j][k])i=anc[i][k],j=anc[j][k];
    }
    return anc[i][0];
}
int find(int x){
    if(fa[x]==x)return x;
    return fa[x]=find(fa[x]);
}
void dfs(int u,int f,int root){
    anc[u][0]=f;
    for(int k=1;k<=16;k++)
    anc[u][k]=anc[anc[u][k-1]][k-1];
    son[root]++;dep[u]=dep[f]+1;fa[u]=root;vis[u]=1;
    insert(rt[f],rt[u],1,m,a[u]);
    for(int i=head[u];i;i=edge[i].nxt){
        int v=edge[i].to;
        if(v!=f)dfs(v,u,root);
    }
    return;
}
int main(){
    read();
    n=read();int e=read(),T=read(),last=0;
    for(int i=1;i<=n;i++)
    a[i]=b[++m]=read(),fa[i]=i;
    LSH();
    for(int i=1;i<=e;i++){
    int x=read(),y=read();
    add(x,y);add(y,x);
    }
    for(int i=1;i<=n;i++)
    if(!vis[i]){
        dfs(i,0,++tot);fa[tot]=tot;
    }
    for(int i=1;i<=T;i++){
    char ch=getchar();
        while(ch!=‘Q‘&&ch!=‘L‘)ch=getchar();
    if(ch==‘Q‘){
        int x=read()^last,y=read()^last,k=read()^last;
        int t=LCA(x,y),ft=anc[t][0];
        printf("%d\n",last=b[query(rt[x],rt[y],rt[t],rt[ft],1,m,k)]);
    }else{
        int x=read()^last,y=read()^last;
        add(x,y);add(y,x);
        int u=find(x),v=find(y);
        if(son[u]<son[v]){
        swap(u,v);
        swap(x,y);
        }
        dfs(y,x,u);
    }
    }
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

原文地址:https://www.cnblogs.com/luyouqi233/p/8510740.html

时间: 2024-10-07 20:54:41

BZOJ3123:[SDOI2013]森林——题解的相关文章

【主席树 启发式合并】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]森林

题面传送门 复出的第一道题.. md就遇到坑了.. 简单来说就是可持久化线段树+启发式合并啊.. 感觉启发式合并好神奇好想学 每一次建边就暴力合并,每一个节点维护从根到它的权值线段树 按照题面的话最省空间的做法就是垃圾回收,但是实在是太慢了.. 而且这题有坑,题面说的是多组数据其实只有一组 而且是$T>1$的一组.. 然后看给了512MB就不需要垃圾回收,而且很多预处理都tm不用了呢!wqnmdsy #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,

[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 对于每一个第一类

Luogu_P3302 [SDOI2013]森林【题解】主席树 lca 启发式合并

# Luogu_P3302 [SDOI2013]森林 主席树,启发式合并,lca luogu题面 求树上路径的第k大,树之间还有合并. 明显是主席树再加合并. 先说链上第k大,其实就是$Tx+Ty-Tlca-Tlcafa$ $T$表示权值线段树. 主席树维护的是从根节点到当前节点的前缀和. ask的代码如下: inline int ask(int x,int y,int lcc,int lcf,int l,int r,int k){ if(l==r) return b[l]; int lz=su

bzoj 3198: [Sdoi2013]spring 题解

[原题] 3198: [Sdoi2013]spring Time Limit: 40 Sec  Memory Limit: 256 MB Submit: 253  Solved: 95 Description Input Output Sample Input 3 3 1 2 3 4 5 6 1 2 3 0 0 0 0 0 0 4 5 6 Sample Output 2 HINT [题解]这道题明明是水题,坑了我两天!!!真是伤心.发现哈希都不熟练了. 首先很容易想到是2^6枚举01状态,使得1

[SDOI2013]森林(树上主席树)

[SDOI2013]森林(luogu) Description 题目描述 小Z有一片森林,含有N个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有M条边. 小Z希望执行T个操作,操作有两类: Q x y k查询点x到点y路径上所有的权值中,第k小的权值是多少.此操作保证点x和点y连通,同时这两个节点的路径上至少有k个点. L x y在点x和点y之间连接一条边.保证完成此操作后,仍然是一片森林. 为了体现程序的在线性,我们把输入数据进行了加密.设lastans为程序上一次输出的结果,

BZOJ3669:[NOI2014]魔法森林——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=3669 https://www.luogu.org/problemnew/show/P2387 为了得到书法大家的真传,小 E 同学下定决心去拜访住在魔法森林中的隐 士.魔法森林可以被看成一个包含 n 个节点 m 条边的无向图,节点标号为 1,2,3,…,n,边标号为 1,2,3,…,m.初始时小 E 同学在 1 号节点,隐士则住在 n 号节点.小 E 需要通过这一片魔法森林,才能够拜访到隐士. 魔

[Sdoi2013]森林

/* 平常这种题很常见的思路就是求出dfs序来,然后每次查询的时候就是在主席树上查询 x+y-lca-fa[lca] 的值就行了. 但是这个题要动态的给森林中加边,还是强制在线的,所以就需要考虑换一种方法来维护这个东西. 首先先dfs出每棵树来,然后对于link操作,可以启发式合并两个主席树.这里我们把主席树维护的dfs序变成维护每个点到根的这条路径.所里link的时候假设我们要把x合到y上,那么我们就边dfs x 这棵树,边用当前点的fa作为历史状态的root来更新当前点的root就行了.求l