首页 > 其他分享 >数据结构懒标记时间戳差异问题

数据结构懒标记时间戳差异问题

时间:2024-10-24 12:44:09浏览次数:1  
标签:轻边 标记 int 差异 top son dfn 数据结构 mo

对于数据结构打 lazytag 后节点时空不统一问题的解决

可以看看之前写的一篇文章 线段树初步理解 ,里头初步介绍了懒标记的作用与使用懒标记所带来的时空不统一的问题。实际上是可以将懒标记拓展到其他数据结构上的。

就以经典的 毛毛虫链剖分 举例子,就是现在我要给树上的与给定的两个点之间路径所经过的所有点相连接边先乘以 \(x\) 然后再给经过的边加上 \(y\) ,由于这这两步操作并没有结合律,因此我不能合并操作,利用之前 NOI2021 d1t1 的技巧,我轻重链剖分后需要维护轻边,那么我把对轻边的影响直接记在轻边的父点上,这样一次性可以改一坨轻边。
这里就十分像线段树了,就相当于一个点管了很多个轻边,而对这一坨轻边的修改本质上就是相当于在这个点上打 tag ,然后就会遇到一个问题,就是我询问单点的时候我不知道每个点同步到父亲的哪一个历史状态(也有可能是现在状态),因此我就需要给每个轻边维护一个 tag 表示同步到其父点的那个历史版本,然后询问到轻边的时候就需要对着父点的 tag 来更新自己的状态就行了。
然后遇到了问题,我可能遇到乘以 0 的操作,因此我需要在多维护一个上一次修改成 0 的时间戳是什么时候,这就有点像历史断代一样,那么现在就有 大历史 和 小历史 , 小历史 在 大历史 里面,然后乘以某个数就是推进 小历史 ,而乘以 0 就是换 大历史。你大概就是在每个点被访问的时候同步历史即可。

自己弄的定理:
在更新历史的时候,如果一个节点的历史比其父点更加先进,那么可以无视父点的操作,儿子的历史状态会改变当且仅当其被父点更新

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch))f^=ch=='-',ch=getchar();
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return f?x:-x;
}
const int N=1e5+5,mo=1e9+7;
void red(int &x){(x>=mo)?(x-=mo):1;}
inline int qpow(int x,int t){
    int ret=1;
    for(;t;t>>=1,x=x*x%mo)if(t&1)ret=ret*x%mo;
    return ret;
}
int dep[N],top[N],up[N],siz[N],son[N],dfn[N],dfntot,n,m,last[N],cnt[N],E[N],ans1,ans2;
vector<int> G[N];
void dfs1(int u,int fa){
	dep[u]=dep[up[u]=fa]+1;
	siz[u]=1;
	for(int v:G[u])if(v!=fa){
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;
	}
}
void dfs2(int u,int tp){
	dfn[u]=++dfntot;
	top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(int v:G[u])if(v!=son[u]&&v!=up[u]){
		cnt[v]=1;
		dfs2(v,v);
	}
}
struct SegmentTree{
    #define lc p<<1
    #define rc p<<1|1
    #define mid (l+r>>1)
	int val[N<<2],add[N<<2],mul[N<<2];
    void build(int p,int l,int r){
        val[p]=add[p]=0;
        mul[p]=1;
        if(l==r)return;
        build(lc,l,mid);
        build(rc,mid+1,r);
    }
	void pushdown(int p,int l,int r){
		mul[lc]=mul[lc]*mul[p]%mo;
        mul[rc]=mul[rc]*mul[p]%mo;
        add[lc]=add[lc]*mul[p]%mo;
        add[rc]=add[rc]*mul[p]%mo;
        val[lc]=val[lc]*mul[p]%mo;
        val[rc]=val[rc]*mul[p]%mo;
        mul[p]=1;
        red(add[lc]+=add[p]);
        red(add[rc]+=add[p]);
        red(val[lc]+=add[p]*(mid-l+1)%mo);
        red(val[rc]+=add[p]*(r-mid)%mo);
        add[p]=0;
    }
    void modify1(int p,int l,int r,int L,int R,int d){
        if(L<=l&&r<=R){
            mul[p]=mul[p]*d%mo;
            add[p]=add[p]*d%mo;
            val[p]=val[p]*d%mo;
            return;
        }
        pushdown(p,l,r);
        if(L<=mid)modify1(lc,l,mid,L,R,d);
        if(mid<R)modify1(rc,mid+1,r,L,R,d);
    }
    void modify2(int p,int l,int r,int L,int R,int d){
        if(L<=l&&r<=R){
            red(add[p]+=d);
            red(val[p]+=d*(r-l+1)%mo);
            return;
        }
        pushdown(p,l,r);
        if(L<=mid)modify2(lc,l,mid,L,R,d);
        if(mid<R)modify2(rc,mid+1,r,L,R,d);
    }
    void modify3(int p,int l,int r,int L,int R,int d){
        modify1(1,1,n,L,R,0);
        modify2(1,1,n,L,R,d);
    }
    int query(int p,int l,int r,int x){
        if(l==r)return val[p];
        pushdown(p,l,r);
        if(x<=mid)return query(lc,l,mid,x);
        else return query(rc,mid+1,r,x);
    }
    #undef lc
    #undef rc
    #undef mid
}T1,T2,T3;
/*
T1维护重链上的边,每条边被其父点统计
T2维护每个节点上一次清零的时间戳
T3维护每个节点上一次清零开始整体乘的乘积
每个jump边被其父点统计
cnt表示记录上一次清零开始后的jump被父亲影响的lazytag
*/
int update(int x){
    int t=T2.query(1,1,n,dfn[up[x]]);
    if(last[x]<t){
        E[x]=0;
        last[x]=t;
        cnt[x]=T3.query(1,1,n,dfn[up[x]]);
        return 0;
    }
    else if(last[x]==t){
        int hv=T3.query(1,1,n,dfn[up[x]])*qpow(cnt[x],mo-2)%mo;
        cnt[x]=cnt[x]*hv%mo;
        E[x]=E[x]*hv%mo;
        return 0;
    }
    else return 1;
}
void mdf(int x,int y,int c,int p,int tms){
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        if(x!=top[x]){
            T1.modify1(1,1,n,dfn[top[x]],dfn[up[x]],(1+mo-p)%mo);
            T1.modify2(1,1,n,dfn[top[x]],dfn[up[x]],p*c%mo);
        }
        if(son[x]){
            T1.modify1(1,1,n,dfn[x],dfn[x],(1+mo-p)%mo);
        }
        if(p==1){
            T2.modify3(1,1,n,dfn[top[x]],dfn[x],tms);
            T3.modify3(1,1,n,dfn[top[x]],dfn[x],1);
            E[top[x]]=c;
            cnt[top[x]]=1;
            last[top[x]]=tms;
        }
        else{
            T3.modify1(1,1,n,dfn[top[x]],dfn[x],(1+mo-p)%mo);
            int t=update(top[x]);
            E[top[x]]=E[top[x]]*(1+mo-p)%mo;
            red(E[top[x]]+=p*c%mo);
            if(!t)cnt[top[x]]=cnt[top[x]]*(1+mo-p)%mo;
        }
        x=up[top[x]];
    }
    if(dep[x]>dep[y])swap(x,y);
    if(x!=y){
        T1.modify1(1,1,n,dfn[x],dfn[up[y]],(1+mo-p)%mo);
        T1.modify2(1,1,n,dfn[x],dfn[up[y]],p*c%mo);
    }
    if(son[y])T1.modify1(1,1,n,dfn[y],dfn[y],(1+mo-p)%mo);
    if(x!=top[x]){
        T1.modify1(1,1,n,dfn[up[x]],dfn[up[x]],(1+mo-p)%mo);
    }
    if(p==1){
        T2.modify3(1,1,n,dfn[x],dfn[y],tms);
        T3.modify3(1,1,n,dfn[x],dfn[y],1);
        if(x==top[x]){
            last[top[x]]=tms;
            E[top[x]]=0;
            cnt[top[x]]=1;
        }
    }
    else{
        T3.modify1(1,1,n,dfn[x],dfn[y],(1+mo-p)%mo);
        if(x==top[x]){
            if(x!=1){
                update(top[x]);
                E[top[x]]=E[top[x]]*(1+mo-p)%mo;
            }
        }
    }
}
void dfs3(int u){
    if(son[u]){
    	int e=T1.query(1,1,n,dfn[u]);
        red(ans1+=e);
        red(ans2+=e*son[u]%mo);
        dfs3(son[u]);
    }
    for(int v:G[u])if(v!=up[u]&&v!=son[u]){
        int t=update(v);
        red(ans1+=E[v]);
        red(ans2+=E[v]*v%mo);
        dfs3(v);
    }
}
signed main(){
//	freopen("lkkts.in","r",stdin);
    n=read(),m=read();
    for(int i=2;i<=n;++i){
        int x=read();
        G[x].pb(i);
        G[i].pb(x);
    }
    dfs1(1,0);
    dfs2(1,1);
    T1.build(1,1,n);
    T2.build(1,1,n);
    T3.build(1,1,n);
    T3.modify3(1,1,n,1,n,1);
    for(int i=1;i<=m;++i){
        int x=read(),c=read(),p=read();
        mdf(1,x,c,p,i);
    }
    dfs3(1);
    printf("%lld\n%lld\n",ans1,ans2);
}

标签:轻边,标记,int,差异,top,son,dfn,数据结构,mo
From: https://www.cnblogs.com/chenhx-xcpc/p/18499364

相关文章

  • 【C++篇】栈的层叠与队列的流动:在 STL 的韵律中探寻数据结构的优雅之舞
    文章目录C++栈与队列详解:基础与进阶应用前言第一章:栈的介绍与使用1.1栈的介绍1.2栈的使用1.2.1最小栈1.2.2示例与输出1.3栈的模拟实现第二章:队列的介绍与使用2.1队列的介绍2.2队列的使用2.2.1示例与输出2.3队列的模拟实现2.3.1示例与输出第三章:优先队......
  • 第十三章_数据结构与集合源码二
    目录8.Map接口分析8.1哈希表的物理结构8.2HashMap中数据添加过程8.2.1JDK7中过程分析8.2.2JDK8中过程分析8.3HashMap源码剖析8.3.1JDK1.7.0_07中源码1、Entry2、属性3、构造器4、put()方法8.3.2JDK1.8.0_271中源码1、Node2、属性3、构造器4、put()方法......
  • 【数据结构】-数组
    数组特点:数组的地址连续,可以通过下标获取数据。1.数组扩容步骤:$1.创建一个比原来数组更长的新数组$2.让原来数组当中的数据依次复制到新数组当中$3.让arr指向新数组,原数组空间释放  2.数组插入2.1最后位置插入$1.判断数组当中有没有位置,用size记录当......
  • 新高一暑假第一期集训恢复性训练【数据结构-晚测】(并查集)(补)
    新高一暑假第一期集训恢复性训练【数据结构-晚测】(并查集)(补)[CF1290C]PrefixEnlightment带权扩展域并查集。任意三个子集的交集为空集,显然,一个点最多只能在两个集合中出现,这样所有集合的大小之和是\(\Theta(n)\)的。一个在两个集合中出现的点ii相当于连接了\(2\)个集合......
  • [数据结构] 删除单链表中最小值结点(C语言版本)
    如果对单链表基本操作或概念不理解的可以跳转:单链表的基本操作(C语言版)-CSDN博客https://blog.csdn.net/m0_74181956/article/details/143082621?spm=1001.2014.3001.5501算法思想:如图所示:定义指针p为L的第一个结点,pre为L的头结点,min为记录每次遍历的最小值结点,minpre为记......
  • 数据结构C语言版_队列笔记||已测试所有代码均可运行
    队列源文件使用markdown编写,CSDN文章发布好像有部分语法改变。每一部分我都有加一个返回标题好像不能使用了。但是CSDN自带一个目录总结,你们无视掉我写的目录直接用CSDN的吧。总结笔记不易,如果有帮助到你希望点个赞。所有代码均已在VScode中运行过,部分代码块因为格式原因......
  • 【数据结构与算法】之龟兔赛跑算法
    目录一、龟兔赛跑算法1.1阶段11.2阶段21.3为什么呢?二、判断是否有环2.1问题描述2.2示例2.3 代码实现三、找到环入口3.1问题描述3.2示例3.3代码实现总结本文主要介绍以环形链表相关的算法,主要采用Java语言实现,这类算法又称Floyd'sTortoiseandHare......
  • 数据结构-----------栈和队列后续
    1队列1.1概念与结构概念:只允许在一端进行插入数据操作,在另一端进行删除元素特殊的线性表,队列具有先进先出的性质入队列:进行插入操作的的一端叫做队尾出队列:进行删除操作的一端叫做队头下面我们来看一下队列的实现队列其实跟单链表是差不多的只不过队列只允许在队尾插入数......
  • 【数据结构】队列(环形缓冲区)的实现
    在学习驱动的过程中,接触到了环形缓冲区的概念,发现这个缓冲区和数据结构中的队列具有惊人的相似之处,因此借此来复习相关知识如果应用层来不及读取数据时,我们可以先将数据放入环形缓冲区中用来记录数据,防止数据丢失。当然,缓冲区越大,那么可以缓存的数据就越多。1.队列的定义队......
  • 【数据结构与算法】之链表经典算法大集合
    本文主要内容是几个关于链表的初级经典算法的分享,都采用Java语言实现,话不多说,立马开始!注意:以下代码有关链表的算法实现均基于以下链表节点类://链表节点类publicclassListNode{intval;ListNodenext;ListNode(){}ListNode(intval){this.val=v......