首页 > 其他分享 >[BZOJ3786] 星系探索 题解

[BZOJ3786] 星系探索 题解

时间:2024-01-19 22:55:27浏览次数:35  
标签:pp 星系 rs int 题解 tr ls define BZOJ3786

题目链接:\(BZOJ\)
本题通过 \(dyf\_DYF\) 的题解理解 \(ETT\),代码则借鉴 \(lcyfrog\) 的题解,图片则使用了何太郎的题解。在此笔者感谢这三位神犇。


声明变量:
\(ls\):左儿子
\(rs\):右儿子
\(sz\):子树大小
\(rk\):对应堆值
\(fa\):节点父亲
\(sm\):子树权值和
\(p\):\(1/-1\) 表示 第一个欧拉序/第二个欧拉序,下面会讲
\(w\):该节点自身权值
\(tp\):子树 \(p\) 之和
\(tg\):懒标记


零、题目大意

给你一颗有根树,需要完成以下三个操作:

  • 计算一个点到根节点路径上的权值和
  • 将一颗子树权值全部增加一个定值
  • 将一颗子树接到另一个节点下面

一、前置知识:欧拉序

欧拉序有两种,我们分开来讲:

(1)第一类欧拉序

考虑用 \(dfs\) 的走法走满整棵树,每到一个节点欧拉序末尾加一个数。
不过,与 \(dfs\) 序不同的是,每遍历完一棵子树后,就得再加一次。
心动不如画图,我们可以通过这张图来理解:

根据图中走法,很容易得出欧拉序为:\(1,2,4,7,4,8,4,9,4,2,5,2,1,3,6,3,1\),可以发现,有 \(2n-1\) 个节点。
这种欧拉序可以快速求出两点最近公共祖先,但与此题无关,关键在第二个。

(2)第二类欧拉序

与上一类不同的是,本类欧拉序是在进入和离开时在末尾增加。
再来一张图:

欧拉序为:\(1,2,4,7,7,8,8,9,9,4,5,5,2,3,6,6,3,1\),有 \(2n\) 个节点。


二、初步思考

我们发现本题要求从一个点到根的路径上的权值和,还有子树加,于是考虑可爱的欧拉序。
发现假如将第二类欧拉序中,离开时增加的节点们全部标记为负,欧拉序维护权值,那么从 \(x\) 到根节点的权值和即为从第一个节点到 \(x\) 第一次出现的位置的和。
这下,我们就可以把树上问题转化为序列问题了。
在这里,我们再声明两个变量,以方便叙述:
\(dfn\):第一次出现位置
\(low\):第二次出现位置
那么对 \(x\) 子树加上 \(y\) 就可以变成在 \(dfn[x]+y\),在 \(low[x]-y\)。


三、加入平衡树

考虑将一个子树接在另一个点上的操作。
发现在移动 \(x\) 的子树接到 \(y\) 下时,只是将 \([dfn[x],low[x]]\) 移动到了 \(dfn[y]\) 后(大家可以自行模拟)。
移动区间位置,单点修改,区间查询,不是线段树,只能是平衡树了。
支持区间操作的平衡树,有 \(fhq\ treap\) 和 \(splay\) 两种,前者似乎更方便一些,因此笔者用了 \(fhq\ treap\)。
当然,如果你对 \(splay\) 有着深厚的情感,你也可以看 \(dyf\_DYF\) 对于 \(splay\) 方法的讲解


四、标程

#include<bits/stdc++.h>
#define ll long long
#define ls(x) pl[x].ls
#define rs(x) pl[x].rs
#define sz(x) pl[x].sz
#define rk(x) pl[x].rk
#define fa(x) pl[x].fa
#define sm(x) pl[x].sm
#define p(x) pl[x].p
#define w(x) pl[x].w
#define tp(x) pl[x].tp
#define tg(x) pl[x].tg
#define N 300005
using namespace std;
struct fhq{
    int ls,rs,sz,rk,fa;
    ll sm,p,w,tp,tg;
}pl[N];int fhq_fail,dfn[N],low[N],f;
int make_node(int x,int y){
    int a=++fhq_fail;
    sz(a)=1;w(a)=sm(a)=x*y;
    rk(a)=rand();p(a)=tp(a)=y;
    return a;
}struct fhq_treap{
    int rt;fhq_treap(){rt=0;}
    void pushup(int pp){
        sz(pp)=sz(ls(pp))+sz(rs(pp))+1;
        tp(pp)=tp(ls(pp))+tp(rs(pp))+p(pp);
        sm(pp)=sm(ls(pp))+sm(rs(pp))+w(pp);
        if(ls(pp)) fa(ls(pp))=pp;
        if(rs(pp)) fa(rs(pp))=pp;
    }void tag(int q,ll pp){
        w(q)+=p(q)*pp;
        sm(q)+=tp(q)*pp;
        tg(q)+=pp;
    }void pushdown(int pp){
        if(!tg(pp)) return;
        tag(ls(pp),tg(pp));
        tag(rs(pp),tg(pp));
        tg(pp)=0;
    }void spilt(int pp,int a,int &x,int &y){
        if(!pp){x=y=0;return;}pushdown(pp);
        if(a>sz(ls(pp))) x=pp,spilt(rs(pp),a-sz(ls(pp))-1,rs(x),y);
        else y=pp,spilt(ls(pp),a,x,ls(y));pushup(pp);
    }int merge(int x,int y){
        pushdown(x);pushdown(y);
        if(!x||!y) return x+y;
        if(rk(x)<rk(y)){
            rs(x)=merge(rs(x),y);
            pushup(x);return x;
        }ls(y)=merge(x,ls(y));
        pushup(y);return y;
    }int rank(int x){
	    int re=sz(ls(x))+1;
	    while(fa(x)){
	    	if(rs(fa(x))==x)
                re+=sz(ls(fa(x)))+1;x=fa(x);
        }return re;
    }void add(int a,ll b){
	    int x,y,z;
	    f=1;spilt(rt,rank(dfn[a])-1,x,z);fa(x)=fa(z)=0;f=0;
	    spilt(z,rank(low[a]),y,z);fa(y)=fa(z)=0;
	    tag(y,b);rt=merge(merge(x,y),z);fa(rt)=0;
	}void work(int a,int b){
        int x,y,z;
        spilt(rt,rank(dfn[a])-1,x,z);fa(x)=fa(z)=0;
        spilt(z,rank(low[a]),y,z);fa(y)=fa(z)=0;
        rt=merge(x,z);fa(rt)=0;spilt(rt,rank(dfn[b]),x,z);
        rt=merge(merge(x,y),z);fa(rt)=0;
    }
}tr;int n,t,b[N];vector<int>g[N];
void dfs(int x){
    dfn[x]=make_node(b[x],1);tr.rt=tr.merge(tr.rt,dfn[x]);
    for(int i=g[x].size()-1;i>=0;i--) dfs(g[x][i]);
    low[x]=make_node(b[x],-1);tr.rt=tr.merge(tr.rt,low[x]);
}signed main(){
	srand(20040523);cin>>n;
    for(int i=2;i<=n;i++){
        int x;cin>>x;
        g[x].push_back(i);
    }for(int i=1;i<=n;i++) cin>>b[i];
    dfs(1);cin>>t;while(t--){
        char c;int d;ll e;cin>>c;
        if(c=='Q'){
            int x,y;cin>>d;
            tr.spilt(tr.rt,tr.rank(dfn[d]),x,y);
            cout<<sm(x)<<"\n";tr.merge(x,y);
        }if(c=='C') cin>>d>>e,tr.work(d,e);
        if(c=='F') cin>>d>>e,tr.add(d,e);
    }return 0;
}//Kaká

标签:pp,星系,rs,int,题解,tr,ls,define,BZOJ3786
From: https://www.cnblogs.com/chang-an-22-lyh/p/17975685/bzoj3786-xingxitansuo-tj

相关文章

  • P4345 [SHOI2015] 超能粒子炮·改 题解
    P4345[SHOI2015]超能粒子炮·改题解求\[\sum_{i=0}^k\binom{n}{i}\pmod{2333}\]思路这种模数小的组合数计数问题可以考虑Lucas定理,试试呗。如果按余数分类不好优化,可以按商分类求和,这样一来套个前缀和可以得到一个递推式,注意最后一块商可能是不整的,单独拿出来即可。......
  • GD动角题解(2024.1.19)
    $upd:$2024.1.19改正了一些错误题目讲解只看第三题若在三角板开始转动的同时,射线\(OC\)也绕点\(O\)以每秒25°的速度逆时针旋转一周,从旋转开始多长时间,射线\(OC\)平分\(∠BOD\)?最重要的一点:动角角度\(=\)初始值\(+\)角度\((vt)\)明确了这一点之后我们看题这题可以分......
  • 题解:阶乘
    题目大意给定一个数\(n\),求两个数\(a\),\(b\),使\(\Large\frac{a!}{b!}\normalsize=n(a>b)\)。若有无数组解输出-1。多组数据。思路简析\[a!=a\times(a-1)\times(a-2)\times\cdots\times2\times1\]\[b!=b\times(b-1)\times(b-2)\times\cdots\times2\times1\]......
  • 【LGR-172-Div.4】洛谷入门赛 #19 题解
    比赛链接:\(link\)T1分饼干I题目描述洛谷网校举行了期末考试,同学们经过课程的学习,考出了优异的成绩。Z在考试中获得了第一名,yz在考试中获得了第二名,老师决定买一些饼干奖励两名小朋友。老师买了三盒饼干,第一盒有\(a\)块饼干,第二盒有\(b\)块饼干,第三盒有\(c\)块饼干......
  • 题解 CF1909H
    题意给定一个长度为\(n\)的排列\(p\)。你可以进行不超过\(10^6\)次操作,每次操作是选择一个长度为偶数的区间\([l,r]\),然后交换\((p_l,p_{l+1}),(p_{l+2},p_{l+3}),...,(p_{r-1},p_r)\)。你需要将排列排序。数据范围:\(n\le3\times10^5\)。题解刚才有个群友问我Z......
  • $20240119$ 练习题解
    \(20240119\)练习题解CF472D通过尝试我们容易发现,与点\(1\)最近的点一定是直接儿子。我们要是把它作为突破点,那就成功了一半了。假设点\(2\)与点\(1\)最近,又假设我们可以用函数\(F(x)\)来确定\(x\)点的子树形态。那我们会发现如果我们还有剩余的节点,那么剩余的节点......
  • 洛谷 P9869 [NOIP2023] 三值逻辑 题解
    Solution模拟程序,容易发现每个点最后的取值都是定值或一个点的初始值(可能是该值取反)。最后是定值的点可以确定初始值,最后取值由该点决定的点也可以确定取值。求出这些取值,答案加上取之为U的点的个数。即第\(i\)个点最后的取值是\(to_i\)的初始值,\(sg_i\)表示是否取反,那......
  • 洛谷 P9751 [CSP-J 2023] 旅游巴士 题解
    Solution能在起点等\(k\)的非负整数倍相当于能在任意点等\(k\)的非负整数倍。由于离开的时间要是\(k\)的负整数倍,将每个点拆成\(k\)个点,\(dis_{i,j}\)表示到了第\(i\)个点长度\(\bmod\text{}k\equivj\)的最短路径。转移时若时间未到,直接在原地等\(k\)的负整......
  • 洛谷 P9745 「KDOI-06-S」树上异或 题解
    Solution树形DP好题。PartI部分分类比下文为简单,我们称一个连通块的权值为连通块内点的异或和。考虑链的部分分,显然可以设\(f_{i}\)是前\(i\)个点所有断边方案的权值和,对于每个点枚举上一条断的边转移。令\(s_i=\bigoplus_{j=1}^{i}a_j\),则\(f_i=\sum_{j=0}^{i-1}(s......
  • 洛谷 P9579「Cfz Round 1」Elevator 另类题解
    一个赛时想到的另类DP做法。Solution容易将原题转化为一个人乘电梯每次上下一层。对于\(a_i<b_i\)是好处理的,记\(\displaystylem=\max_{1\leqi\leqn}\{a_i,b_i\}\),只需要跑到\(m\)即可解决所有这种条件。对于\(a_i>b_i\)的条件,我们除了到\(m\)外,还需要额外地从......