luogu P2137 Gty的妹子树(分块,主席树)

询问的化我们可以建主席树。然后修改?,树套树。。。,最后插入?炸了。
所以我们对操作进行分块。
我们先对整棵树建一个主席树。修改,插入我们先记录下来。然后询问的时候先对主席树查询,然后暴力遍历我们记录下来的修改插入操作。每\(\sqrt{m}\)次操作后我们重新构建一个主席树。这样我们保证了重建主席树和询问的总复杂度为\(O(nlogn\sqrt{m})\)然后就把这道题解决了。
有一个难办的事就是如何记录修改和插入的操作。可以使每次询问的时候我们可以知道修改和插入是否在\(u\)的子树中以便我们判断是否要让这些修改和询问产生贡献。因为没考虑到可以询问插入的节点,在多次尝试后我决定维护一个\(f[i][j]\)代表i向上跳\(i^j\)的深度到达的节点。处理询问的时候我们对于每一个修改和插入都跳到和u一个深度看是否相等。
当然还要记录一些常规的信息比如这个点被修改之前的权值什么的。。。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=101000;
int head[N],cnt;
int n,m,ans,a[N],b[N],CNT,more;
int be[N],ed[N],dep[N],fa[N][25],tot,id[N],top,w[N],from[N];
int root[N],ch[N*20][2],sum[N*20],num;
struct edge{
    int to,nxt;
}e[N*2];
inline void add_edge(int u,int v){
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].to=v;
    head[u]=cnt;
}
inline void add(int l,int r,int x,int pre,int &now){
    now=++num;
    sum[now]=sum[pre]+1;
    ch[now][0]=ch[pre][0];
    ch[now][1]=ch[pre][1];
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x>mid)add(mid+1,r,x,ch[pre][1],ch[now][1]);
    else add(l,mid,x,ch[pre][0],ch[now][0]);
}
inline int check(int l,int r,int L,int R,int pre,int now){
    if(l==L&&r==R)return sum[now]-sum[pre];
    int mid=(l+r)>>1;
    if(L>mid)return check(mid+1,r,L,R,ch[pre][1],ch[now][1]);
    else if(R<=mid)return check(l,mid,L,R,ch[pre][0],ch[now][0]);
    else return check(l,mid,L,mid,ch[pre][0],ch[now][0])+check(mid+1,r,mid+1,R,ch[pre][1],ch[now][1]);
}
inline void dfs(int u,int f){
    be[u]=++tot;
    dep[u]=dep[f]+1;
    add(1,n,lower_bound(b+1,b+1+n,a[u])-b,root[be[u]-1],root[be[u]]);
    fa[u][0]=f;
    for(int i=1;i<=20;i++)fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(v==f)continue;
        dfs(v,u);
    }
    ed[u]=tot;
}
inline bool judge(int x,int to){
    for(int i=20;i>=0;i--)
        if(dep[fa[x][i]]>=dep[to])x=fa[x][i];
    if(x==to)return true;
    else return false;
}
inline int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0';ch=getchar();}
    return sum*f;
}
int main(){
    n=read();
    for(int i=1;i<n;++i){
        int u=read(),v=read();
        add_edge(u,v);add_edge(v,u);
    }
    for(int i=1;i<=n;++i)a[i]=read();
    m=read();
    int Block=sqrt(m*25);
    while(m--){
        int type=read(),u=read()^ans,x=read()^ans;
        if(CNT==0){
            tot=0;top=0;num=0;n+=more;more=0;
            for(int i=1;i<=n;i++)b[i]=a[i];
            sort(b+1,b+1+n);
            dfs(1,1);
        }
        if(type==0){
            if(u<=n){
                int tmp=upper_bound(b+1,b+1+n,x)-b;
                if(tmp<=n)ans=check(1,n,tmp,n,root[be[u]-1],root[ed[u]]);
                else ans=0;
            }
            else ans=0;
            for(int i=1;i<=top;++i)
                if(judge(id[i],u)){
                    if(from[i]>x)ans--;
                    if(w[i]>x)ans++;
                }
            for(int i=n+1;i<=n+more;++i){
                if(judge(i,u)){
                    if(w[i]>x)ans++;
                }
            }
            printf("%d\n",ans);
            CNT++;
        }
        else if(type==1){
            ++top;
            w[top]=x;from[top]=a[u];id[top]=u;
            ++CNT;
            a[u]=x;
        }
        else{
            ++more;
            add_edge(n+more,u);
            add_edge(u,n+more);
            a[n+more]=x;
            fa[n+more][0]=u;dep[n+more]=dep[u]+1;
            for(int i=1;i<=20;i++)fa[n+more][i]=fa[fa[n+more][i-1]][i-1];
            w[n+more]=x;
            ++CNT;
        }
        if(CNT==Block)CNT=0;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Xu-daxia/p/10124264.html

时间: 2024-11-09 10:19:10

luogu P2137 Gty的妹子树(分块,主席树)的相关文章

归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

[可持久化线段树(主席树)]

主席树 抛出问题 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示序列的长度和查询的个数. 第二行包含N个整数,表示这个序列各项的数字. 接下来M行每行包含三个整数l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值. 输出格式: 输出包含k行,每行1个整数,依次表示每一次查询的结果 解决问题 主席树(可持久化线段树)法 于是针对这个问题,新的数据结构诞生了,也就是主席树. 主席树本名

【bzoj3744】Gty的妹子序列 分块+树状数组+主席树

题目描述 我早已习惯你不在身边, 人间四月天 寂寞断了弦. 回望身后蓝天, 跟再见说再见…… 某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现 她们排成了一个序列,每个妹子有一个美丽度. Bakser神犇与他打算研究一下这个妹子序列,于是Bakser神犇问道:"你知道区间 [l,r]中妹子们美丽度的逆序对数吗?" 蒟蒻Autumn只会离线乱搞啊……但是Bakser神犇说道:"强制在线." 请你帮助一下Autumn吧.

BZOJ 3744 Gty的妹子序列 分块+fenwick

题目大意:强制在线区间无修改逆序对. 思路:看到数据范围发现分块是很显然的.预处理了很多东西,比如说每个块里面的逆序对个数,还有f[i][j]表示从第i块到第j块的逆序对个数.如果仅仅处理到这里的话,后面是不太好处理的.我们还需要一个东西,是每个点对每个块的逆序对个数,并取前缀合优化.否则的话就得用主席树来乱搞,那常数 反正总时间复杂度大概是O(n*sqrt(n)*logn)  强行不到O(n^2) 剩下就是小事了, 比如离散话啥的.. CODE: #include <cmath> #incl

【模板】【数据结构】【树】主席树

技巧一:离散去重 for(int i=1;i<=n;i++) scanf("%d",&a[i] ),b[i]=a[i]; sort(b+1,b+n+1); int nn=unique(b+1,b+n+1)-b-1;//假设有x个数,那么nn指针会停在第x+1个数的位置 ,nn及以后的都是重复的元素for(int i=1;i<=n;i++) id[i]=lower_bound(b+1,b+nn+1,a[i])-b;//离散过后的新value 技巧二:可持久化数据结构

可持续化线段树(主席树)

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

神奇的树(主席树思想的应用)

主席树这个概念应该不陌生吧!恩?不会, 戳这里. 主席树(函数式线段树)用的是函数思想,一个节点开数组用来保存自己的左右节点,这样节省许多不必要的空间,还可以保存许多历史状态.而这里我们用的是主席树的函数思想来实现. 上题:http://acm.hdu.edu.cn/showproblem.php?pid=5444 题目大意: 给你一个序列,第一个数为二叉树根节点,之后每个数往上加节点,且保证左节点小于根节点,且保证右节点大于根节点.且每个节点最多有2个子节点.然后再查询位置,每往左找输出一个E

洛谷 [P3834] 可持久化线段树(主席树)

主席树可以存储线段树的历史状态,空间消耗很大,一般开45n即可 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <queue> #define lson l, mid #define rson mid+1, r #define ll long long using name

静态可持久化线段树(主席树)

题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示序列的长度和查询的个数. 第二行包含N个正整数,表示这个序列各项的数字. 接下来M行每行包含三个整数 l, r, kl,r,k , 表示查询区间 [l, r][l,r] 内的第k小值. 输出格式: 输出包含k行,每行1个正整数,依次表示每一次查