【BZOJ3720】Gty的妹子树(主席树+时间分块)
大致题意: 给你一棵有根树,让你支持三种操作:询问某子树中大于\(x\)的值的个数,把某一节点值改成\(x\),添加一个父节点为\(u\)、权值为\(x\)的节点。
关于此题做法
此题做法真的是五花八门啊,主要做法貌似有四种:
- 平衡树套平衡树。似乎没有人写。
- 树分块。官方正解,实际上能被菊花图卡掉,但是没有构造数据卡。
- 时间分块。即按照操作的次数进行分块,这也是我的做法。
- 代码分块。即分类讨论。
下面,我们主要讲一讲如何用时间分块处理这题。
主席树的暴力做法
我们先来了解一下如何用主席树来暴力求解此题。
首先,我们\(dfs\)遍历一遍题目中给出的树,将其\(dfs\)序存储下来。
然后,我们用主席树对其进行维护,每次可以轻松求出小于\(k\)的数的个数。
对于修改操作,我们可以... ...直接重构一棵主席树... ...
不难发现,这种方法显然过于暴力,可以被轻松卡掉。
时间分块
不停地重构的确太费时间,那么我们能不能不重构呢?
显然不重构也不可以,依然容易被卡。
所以,我们就要考虑时间分块,即隔一段时间重构一次,从而保证复杂度。
如何处理修改操作
既然要隔时间重构,我们就要想一个办法来处理修改操作。
对于添加节点操作,我们可以记录一下它的祖先中深度最大的一个位于树中的节点\(g_i\)。
对于修改元素值操作,如果是没有加入树中的新节点,暴力修改即可。否则我们可以考虑用一个栈,将所有被修改过值的元素存储下来,并记录下其被修改后的值。
如何处理询问操作
对于询问操作,如果该节点不在树中,我们可以直接暴力求解。
否则,我们首先用主席树求出已经在树中的元素的答案。对于没有加入到树中的节点,我们只需判断\(g_i\)是否处于当前询问节点的子树中,就可以判断该节点是否会对答案造成贡献了。而对于既在栈中又在当前询问子树中的节点,我们考虑先减去其原先值的贡献,然后再计算新值的贡献。
关于重构
前面已经说了,隔一段时间,我们就要将这段时间内的修改操作全部更新到树上去,从而保证复杂度。
我们具体操作的事情有以下几件:
- 将栈中的元素值全部修改到原元素上,然后清空栈。
- 重求一遍\(dfs\)序。
- 更新主席树要用的离散化数组。
- 重构主席树。
这应该都还是比较容易实现的。
其实,重构的过程与初始化的过程完全可以放在同一个函数中!
这样一来,这道题就轻松解决了。
注意,这道题有比较多的细节,一不小心就会写炸!
具体实现见代码。
代码
#include<bits/stdc++.h>
#define ten(x) (((x)<<3)+((x)<<1))
#define N 100000
#define LogN 20
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,top=0,ee=0,g[N+5],Val[N+5],lnk[N+5],Stack[N+5],NewVal[N+5],InStack[N+5];
struct edge
{
    int to,nxt;
}e[N+5];
class FIO
{
    private:
        #define Fsize 100000
        #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
        #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
        int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
    public:
        FIO() {A=B=Fin;}
        inline void read(int &x) {x=0;while(!isdigit(ch=tc()));while(x=ten(x)+(ch&15),isdigit(ch=tc()));}
        inline void write(int x) {if(!x) return (void)(pc('0'));while(x) Stack[++Top]=x%10+48,x/=10;while(Top) pc(Stack[Top--]);}
        inline void write_char(char x) {pc(x);}
        inline void clear() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class Class_ChairmanTree//主席树
{
    private:
        int n,tot,Root[N+5];
        struct node
        {
            int Size,Son[2];
            inline void Clear() {Size=Son[0]=Son[1]=0;}
        }node[N*LogN+5];
        inline void Build(int l,int r,int &rt)
        {
            if(node[rt=++tot].Clear(),!(l^r)) return;
            register int mid=l+r>>1;
            Build(l,mid,node[rt].Son[0]),Build(mid+1,r,node[rt].Son[1]);
        }
        inline void ins(int l,int r,int &rt,int lst,int val)
        {
            if(node[rt=++tot]=node[lst],++node[rt].Size,!(l^r)) return;
            register int mid=l+r>>1;
            val<=mid?ins(l,mid,node[rt].Son[0],node[lst].Son[0],val):ins(mid+1,r,node[rt].Son[1],node[lst].Son[1],val);
        }
        inline int qry(int l,int r,int rt1,int rt2,int val)
        {
            if(!(l^val)) return node[rt2].Size-node[rt1].Size;
            register int mid=l+r>>1;
            if(val<=mid) return qry(l,mid,node[rt1].Son[0],node[rt2].Son[0],val)+node[node[rt2].Son[1]].Size-node[node[rt1].Son[1]].Size;
            return qry(mid+1,r,node[rt1].Son[1],node[rt2].Son[1],val);
        }
    public:
        inline void Init(int len) {tot=0,Build(1,n=len,Root[0]);}
        inline void Insert(int v,int val) {ins(1,n,Root[v],Root[v-1],val);}
        inline int Query(int ql,int qr,int val) {return qry(1,n,Root[ql-1],Root[qr],val);}
}ChairmanTree;
class Class_DfnSolver//求DFS序
{
    public:
        int d,dfn[N+5],fac[N+5],Size[N+5];
    private:
        inline void dfs(int x,int lst)
        {
            register int i;
            for(Size[fac[dfn[x]=++d]=x]=1,i=lnk[x];i;i=e[i].nxt)
                if(e[i].to^lst) dfs(e[i].to,x),Size[x]+=Size[e[i].to];
        }
    public:
        inline void Init() {dfs(1,d=0);}
        inline bool Include(int x,int y) {return dfn[x]<=dfn[y]&&dfn[y]<dfn[x]+Size[x];}//求y是否在x的子树中
}DfnSolver;
class Discretization//离散化
{
    private:
        int num[N+5];
    public:
        int cnt;
        inline void Init(int len,int *data)
        {
            for(register int i=1;i<=len;++i) num[i]=data[i];
            sort(num+1,num+len+1),cnt=unique(num+1,num+len+1)-num-1;
        }
        inline int get_val(int x)
        {
            register int l=1,r=cnt,mid=l+r>>1;
            for(;l<=r;mid=l+r>>1) num[mid]<x?l=mid+1:r=mid-1;
            return l;
        }
        inline bool Include(int x) {return num[cnt]>=x;}
}D;
inline int BruteForce(int x,int val)//暴力求解未加入树中节点的答案
{
    register int i,res=Val[x]>=val;
    for(i=lnk[x];i;i=e[i].nxt) res+=BruteForce(e[i].to,val);//统计答案
    return res;
}
inline void ReBuild()//初始化&重构
{
    while(top) Val[Stack[top]]=NewVal[Stack[top]],InStack[Stack[top--]]=0;//将栈中的元素值全部修改到原元素上,然后清空栈
    DfnSolver.Init(),D.Init(n=m,Val),ChairmanTree.Init(D.cnt);//重求一遍dfs序,并更新主席树要用的离散化数组
    for(register int i=1;i<=n;++i) ChairmanTree.Insert(i,D.get_val(Val[DfnSolver.fac[i]]));//重构主席树
}
int main()
{
    register int i,Q,op,x,y,ans=0,time_tot=0,Size=sqrt(N*LogN);
    for(F.read(n),m=n,i=1;i<n;++i) F.read(x),F.read(y),add(x,y),add(y,x);
    for(i=1;i<=n;++i) F.read(Val[i]);
    for(ReBuild(),F.read(Q);Q;--Q)//初始化
    {
        F.read(op),F.read(x),F.read(y),x^=ans,y^=ans;//注意强制在线
        switch(op)
        {
            case 0:
                if(++y,x>n) {F.write(ans=BruteForce(x,y)),F.write_char('\n');break;}//对于未被加入树中的节点,暴力求解答案
                ans=D.Include(y)?ChairmanTree.Query(DfnSolver.dfn[x],DfnSolver.dfn[x]+DfnSolver.Size[x]-1,D.get_val(y)):0;//先求出树中节点的答案
                for(i=n+1;i<=m;++i) if(DfnSolver.Include(x,g[i])&&Val[i]>=y) ++ans;//对于没有加入到树中的节点,判断g[i]是否处于当前子树中
                for(i=1;i<=top;++i) if(DfnSolver.Include(x,Stack[i])) Val[Stack[i]]>=y&&--ans,NewVal[Stack[i]]>=y&&++ans;//对于既在栈中又在当前询问子树中的节点,先减去其原先值的贡献,然后再计算新值的贡献
                F.write(ans),F.write_char('\n');
            break;
            case 1:
                if(x>n) {Val[x]=y;break;}//对于未加入到树中的节点,直接修改
                if(!InStack[x]) InStack[Stack[++top]=x]=1;//如果不在栈中,将其加入栈中
                NewVal[x]=y;//存储下新的值
            break;
            case 2:
                Val[++m]=y,add(x,m),g[m]=x<=n?x:g[x];//加入一个节点,求出g[x]
            break;
        }
        if(op&&++time_tot>=Size) ReBuild(),time_tot=0;//当修改操作达到一定次数时重构
    }
    return F.clear(),0;
}
【BZOJ3720】Gty的妹子树(主席树+时间分块)的更多相关文章
- bzoj 3744: Gty的妹子序列 主席树+分块
		3744: Gty的妹子序列 Time Limit: 15 Sec Memory Limit: 128 MBSubmit: 101 Solved: 34[Submit][Status] Descr ... 
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
		线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ... 
- 【BZOJ3439】Kpm的MC密码 trie树+主席树
		Description 背景 想Kpm当年为了防止别人随便进入他的MC,给他的PC设了各种奇怪的密码和验证问题(不要问我他是怎么设的...),于是乎,他现在理所当然地忘记了密码,只能来解答那些神奇的身 ... 
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
		“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ... 
- UOJ#218. 【UNR #1】火车管理 线段树 主席树
		原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ... 
- [学习笔记]  可持久化线段树&主席树
		众所周知,线段树是一个非常好用也好写的数据结构, 因此,我们今天的前置技能:线段树. 然而,可持久化到底是什么东西? 别急,我们一步一步来... step 1 首先,一道简化的模型: 给定一个长度为\ ... 
- 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))
		函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ... 
- HDU5790 Prefix 字典树+主席树
		分析:这个题和spoj的d_query是一个题,那个是求一段区间里有多少个不同的数字,这里是统计有多少个不同的前缀 用字典树进行判重,(和查询不同的数字一样)对于每个不同的前缀,只保留它最后一次出现的 ... 
- bzoj 3545&&3551: [ONTAK2010]Peaks &&加强版 平衡树&&并查集合并树&&主席树
		3545: [ONTAK2010]Peaks Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 635 Solved: 177[Submit][Stat ... 
- BZOJ 4539: [Hnoi2016]树 [主席树 lca]
		4539: [Hnoi2016]树 题意:不想写.复制模板树的子树,查询两点间距离. *** 终于有一道会做的题了...... 画一画发现可以把每次复制的子树看成一个大点来建一棵树,两点的lca一定在 ... 
随机推荐
- 799C(xjb)
			题目链接: http://codeforces.com/problemset/problem/799/C 题意: 有c, d两种货币, 有 n 个货物, 可以用 c 货币或者 d 货币购买, 现在需要 ... 
- [Xcode 实际操作]四、常用控件-(17)为MKMapView地图上显示提示框
			目录:[Swift]Xcode实际操作 本文将演示当点击地图上的标注圆点时,弹出信息窗口. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] import UIKit ... 
- Jdk升级到11引起的问题:程序包javax.xml.bind.annotation不存在
			Jdk12 都发布了, 我也下载一个玩一玩吧.刚准备要下载,发现之前已经下载了一个11, 那就11 吧,也不用太新了. 安装了jdk11,习惯性的设置了一下环境变量: JAVA_HOME=D:\too ... 
- Java Script 第二章.
			对象: JavaScript中的所有事物都是对象:字符串,数组,数值,函数..... JavaScript中提供多个内建对象,比如说 String, Date, Array等等.对象只是带有属性和 ... 
- Idea提示没有符号类错误解决
			将提示没有符号类的文件打开,右键单独编译一次,再重新打包即可解决 
- lynis检测
			https://www.cnblogs.com/ssooking/p/6034402.html Usage: lynis command [options] Command: audit audit ... 
- pytho虚拟环境
			pip install virtualenv 进入项目目录 virtualenv venv 激活venv source venv/bin/activate 
- Docker基础 :网络配置详解
			本篇文章将讲述 Docker 的网络功能,包括使用端口映射机制来将容器内应用服务提供给外部网络,以及通过容器互联系统让多个容器之间进行快捷的网络通信,有兴趣的可以了解下. 大量的互联网应用服务包含多个 ... 
- python学习四(处理数据)
			head first python中的一个数据处理的例子 有四个U10选手的600米成绩,请取出每个选手跑的最快的3个时间.以下是四位选手的9次成绩 James 2-34,3:21,2.34,2.45 ... 
- VMware安装CentOS7的详细过程
			原文:https://www.jianshu.com/p/ce08cdbc4ddb?utm_source=tuicool&utm_medium=referral 本篇文章主要介绍了VMware ... 
