【校内模拟】Growing(树哈希)(SBT)(LCT维护轻重链剖分)

简要题意:

给一棵有根树,强制在线,支持动态加叶子,询问一个子树的自同构变换个数。

题解:

一句话题解:SBT+封装双哈希+原根倒base+LCT维护三个标记

顺便说一下,博主并不清楚有没有什么离线处理方式

以前确实没有见过LCT拿来维护轻重链剖分这种东西的。。。(LCT维护的东西一般称为虚实链剖分)

首先考虑一个显然的暴力,维护树哈希,然后每次加叶子暴力跳所有父亲并修改哈希值,就可以直接维护答案了。

我们发现并不能暴力维护,复杂度显然假完了。

考虑一下答案的表示式,设 c n t [ p ] cnt[p] cnt[p]表示子树中哈希值为 p p p的有多少个, t r [ p ] tr[p] tr[p]表示以 p p p为根的子树内部的自同构变换个数,则 u u u的计算式就是 ∏ c n t [ p ] ! ∏ v ∈ s o n ( u ) t r [ v ] \prod cnt[p]!\prod_{v\in son(u)}tr[v] cnt[p]!vson(u)tr[v]

感觉还是没法做啊。。。。

定义重儿子为 s i z [ v ] ∗ 2 ≥ s i z [ u ] siz[v]*2\geq siz[u] siz[v]2siz[u]的孩子 v v v

显然每个点要么没有重儿子,要么只有一个重儿子,并且重儿子一定不和任何一个其他子树同构。

用LCT维护一条重链,显然如果我们在链中间某个点改了答案,显然只会一路改上去,并且由于出现次数都是1次,改的就只有 t r [ u ] tr[u] tr[u]部分,相当于整条链点的答案乘上一个值。

注意到我们从任何一片叶子开始跳父亲,最多只会跳 O ( log ⁡ n ) O(\log n) O(logn)条轻边。

在这些地方暴力修改即可。

那么问题就只剩下如何维护树哈希值。

由于我们能够保证重儿子只出现了一次,那么直接考虑普通的排序+模质数的进制哈希。

由于要维护排序,所以用一个支持单点插入,单点删除,计数的平衡树即可,直接用SBT即可。

然后把重儿子放到序列的开头算哈希值即可。

为了降低冲突概率,双哈希+base使用原根即可。

进一步降低冲突概率,每一层用不同的原根即可。

当然这些就是细节了。不清楚有没有卡,不写这些应该6K内能写完。

下面的代码不到7.5K 其实是7.49K


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	template<typename T>
	inline T get(){
		char c;T num;
		while(!isdigit(c=gc()));num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
	inline int gi(){return get<int>();}
}
using namespace IO;

using std::cerr;
using std::cout;
#define fi first
#define se second

static cs int M1=1e9+7,M2=1e9+9;
static std::mt19937 R(0);
static int get_gr(cs int M){
	int phi=M-1;std::vector<int> p;
	for(int re i=2;i*i<=phi;++i)
	if(phi%i==0){
		p.push_back(i);
		while(phi%i==0)phi/=i;
	}
	if(phi>1)p.push_back(phi);phi=M-1;
	auto power=[&](int a,int b)->int{
		int r=1;for(;b;b>>=1,a=(ll)a*a%M)
		(b&1)&&(r=(ll)r*a%M);return r;
	};
	for(int re gr=2;;++gr){
		bool flag=true;
		for(int t:p)if(power(gr,phi/t)==1)
		{flag=false;break;}if(flag)return gr;
	}
}
static cs int g1=get_gr(M1),g2=get_gr(M2);
static int gen(int M,int g){
	auto power=[&](int a,int b)->int{
		int r=1;for(;b;b>>=1,a=(ll)a*a%M)
		(b&1)&&(r=(ll)r*a%M);return r;
	};
	while(true){int c=R()%(M-2)+1;
		if(std::__gcd(c,M-1)==1)
		return power(g,c);
	}
}
class Key{
	private:
		int x,y;
	public:
		Key(){}Key(int _x,int _y):x(_x),y(_y){}
		friend Key operator+(cs Key &a,cs Key &b){return Key((a.x+b.x)%M1,(a.y+b.y)%M2);}
		friend Key operator-(cs Key &a,cs Key &b){return Key((a.x-b.x+M1)%M1,(a.y-b.y+M2)%M2);}
		friend Key operator*(cs Key &a,cs Key &b){return Key((ll)a.x*b.x%M1,(ll)a.y*b.y%M2);}
		friend Key &operator+=(Key &a,cs Key &b){return a=a+b;}
		friend Key &operator-=(Key &a,cs Key &b){return a=a-b;}
		friend Key &operator*=(Key &a,cs Key &b){return a=a*b;}
		friend bool operator<(cs Key &a,cs Key &b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
		friend bool operator<=(cs Key &a,cs Key &b){return a.x<b.x||(a.x<=b.x&&a.y<=b.y);}
		friend bool operator==(cs Key &a,cs Key &b){return a.x==b.x&&a.y==b.y;}
		friend bool operator!=(cs Key &a,cs Key &b){return a.x!=b.x||a.y!=b.y;}
};
Key Random(){return Key(gen(M1,g1),gen(M2,g2));} 
cs Key key0(0,0),key1(1,1),keyt(233,233); 

cs int N=3e5+7;

namespace SBT{
	int lc[N],rc[N],siz[N];
	cs double alpha=0.55;
	Key prod[N],base[N],h[N];
	std::pair<Key,int> vl[N];
	inline void pushup(int u){
		siz[u]=siz[lc[u]]+siz[rc[u]]+1;
		prod[u]=key1;h[u]=key0;
		if(lc[u]){
			h[u]+=h[lc[u]];
			prod[u]*=prod[lc[u]];
		}
		h[u]+=vl[u].fi*prod[u];
		prod[u]*=base[u];
		if(rc[u]){
			h[u]+=h[rc[u]]*prod[u];
			prod[u]*=prod[rc[u]];
		}
	}
	inline void Zig(int &u){
		int v=lc[u];lc[u]=rc[v],rc[v]=u;
		pushup(u),pushup(v),u=v;
	}
	inline void Zag(int &u){
		int v=rc[u];rc[u]=lc[v],lc[v]=u;
		pushup(u),pushup(v),u=v;
	}
	
	inline void ins(int &u,int v){
		if(!u)return pushup(u=v);
		if(vl[v]<vl[u]){
			ins(lc[u],v);pushup(u);
			if(siz[lc[u]]>siz[u]*alpha)Zig(u);
		}else {
			ins(rc[u],v);pushup(u);
			if(siz[rc[u]]>siz[u]*alpha)Zag(u);
		}
	}
	
	inline void del(int &u,int v){
		if(!u)return ;
		if(u==v){
			if(!lc[u]||!rc[u]){
				u=lc[u]|rc[u];
				lc[v]=rc[v]=0;
				siz[v]=1;
			}
			else if(siz[rc[u]]>siz[lc[u]]){
				Zag(u),del(lc[u],v);
			}
			else {
				Zig(u),del(rc[u],v);
			}
			if(u)pushup(u);
			return ;
		}
		(vl[v]<vl[u])?del(lc[u],v):del(rc[u],v);pushup(u);
	}
	
	inline int count(int u,cs Key &val,int t=0){
		if(!u)return 0;
		if(vl[u].fi==val){
			return 1+(t==1?siz[lc[u]]:count(lc[u],val,2))
				+(t==2?siz[rc[u]]:count(rc[u],val,1)); 
		}
		if(val<vl[u].fi)return count(lc[u],val,t);
		else return count(rc[u],val,t);
	}
}

cs int mod=998244353;
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;} 
inline void Mul(int &a,int b){a=mul(a,b);}
inline int power(int a,int b){
	int r=1;for(;b;b>>=1,a=mul(a,a))
	(b&1)&&(r=mul(r,a));return r;
}

int d[N],son[N],inv[N],rt[N],mxd;
Key Base[N],dBase[N],rec[N];

inline int Ins(int u,int v,cs Key &h){
	SBT::base[v]=Base[u];
	SBT::vl[v]=std::make_pair(h,v);
	SBT::pushup(v);rec[v]=h;
	int cnt=SBT::count(rt[u],h);
	SBT::ins(rt[u],v);return cnt+1;
}
inline int Del(int u,int v){
	int cnt=SBT::count(rt[u],rec[v]);
	SBT::del(rt[u],v);return inv[cnt];
}
inline int modify(int u,int v,cs Key &newh){
	int res=Del(u,v);return mul(res,Ins(u,v,newh));
}

namespace LCT{
	int son[N][2],fa[N];
	Key h[N],htag[N];
	Key coef[N],prod[N];
	int siz[N],stag[N];
	int ans[N],atag[N];
	inline void init(int u){
		htag[u]=key0;h[u]=keyt;
		stag[u]=0;atag[u]=1;
		siz[u]=ans[u]=1;
		coef[u]=prod[u]=key1;
	}
	inline int &lc(int u){return son[u][0];}
	inline int &rc(int u){return son[u][1];}
	inline void pushup(int u){
		prod[u]=coef[u];
		if(lc(u))prod[u]*=prod[lc(u)];
		if(rc(u))prod[u]*=prod[rc(u)];
	} 
	inline bool isrt(int u){return son[fa[u]][0]!=u&&son[fa[u]][1]!=u;}
	inline bool which(int u){return son[fa[u]][1]==u;}
	inline void Rotate(int u){
		int p=fa[u],pp=fa[p],d=which(u);
		if(!isrt(p))son[pp][which(p)]=u;
		son[p][d]=son[u][!d],fa[son[p][d]]=p;
		son[u][!d]=p,fa[p]=u,fa[u]=pp;
		pushup(p),pushup(u); 
	}
	inline void taghash(int u,Key delta){
		htag[u]+=delta;
		if(rc(u))delta*=prod[rc(u)];
		h[u]+=delta;
	}
	inline void tagans(int u,int delta){
		Mul(atag[u],delta);
		Mul(ans[u],delta);
	}
	inline void tagsiz(int u,int delta){
		siz[u]+=delta;
		stag[u]+=delta;
	}
	inline void pushdown(int u){
		if(htag[u]!=key0){
			if(rc(u))taghash(rc(u),htag[u]),htag[u]*=prod[rc(u)];
			htag[u]*=coef[u];if(lc(u))taghash(lc(u),htag[u]);htag[u]=key0;
		}
		if(stag[u]!=0){
			if(lc(u))tagsiz(lc(u),stag[u]);
			if(rc(u))tagsiz(rc(u),stag[u]);
			stag[u]=0;
		}
		if(atag[u]!=1){
			if(lc(u))tagans(lc(u),atag[u]);
			if(rc(u))tagans(rc(u),atag[u]);
			atag[u]=1;
		}
	}
	int q[N],qn;
	inline void Splay(int u){
		q[qn=1]=u;for(int re p=u;!isrt(p);p=fa[p])q[++qn]=fa[p];
		while(qn)pushdown(q[qn--]);for(int re p;!isrt(u);Rotate(u))
		if(!isrt(p=fa[u]))Rotate(which(u)==which(p)?p:u);
	}
}
using LCT::h;
using LCT::siz;
using LCT::lc;
using LCT::rc;
using LCT::Splay;
using LCT::pushdown;
using LCT::pushup;
using LCT::taghash;
using LCT::tagsiz;
using LCT::tagans; 

int Q,op,ans,pc,n;

inline void updateson(int u,int v,int &ansfac){// v may be the new hson of u 
	pushdown(u);rc(u)=0;pushup(u);tagsiz(u,1);
	int delta=modify(u,v,h[v]);
	if(son[u]){
		int w=son[u];Splay(w);
		if(siz[w]*2<siz[u]){
			Mul(delta,Ins(u,w,h[w]));
			son[u]=0;
		}
	}
	if(siz[v]*2>=siz[u]){
		son[u]=v;Mul(delta,Del(u,v));
	}
	Mul(ansfac,delta);
	tagans(u,ansfac);
	Key newh=key0;
	if(rt[u])newh=SBT::h[rt[u]]*Base[u]*Base[u];
	if(son[u]){
		int w=son[u];Splay(w);
		newh+=h[w]*Base[u];
	}
	Key deltah=newh-h[u];
	taghash(u,deltah);pushdown(u);
	if(son[u]){
		int w=son[u];Splay(w);
		LCT::coef[w]=Base[u];
		pushup(w);pushdown(u);
		rc(u)=w;pushup(u);
	}
}

inline void update(int u,int ansfac){
	int p=u;while(Splay(u),p=LCT::fa[u]){
		while(lc(u))u=lc(u);
		Splay(u),Splay(p);
		updateson(p,u,ansfac);
		u=p;
	}
}

inline void Addnode(){
	int p=gi();if(op)p=(p+ans)%n+1;
	LCT::init(++n);d[n]=d[p]+1;
	if(d[n]>mxd)mxd=d[n],dBase[mxd]=Random();
	Base[n]=dBase[d[n]];LCT::fa[n]=p;
	int ansfac=Ins(p,n,LCT::h[n]);
	update(n,ansfac);
}

inline void Query(){
	int u=gi();if(op)u=(u+ans)%n+1;
	Splay(u);cout<<(ans=LCT::ans[u])<<"\n";
	pc=0;for(int re tmp=ans;tmp;tmp&=tmp-1)++pc;
}

signed main(){
#ifdef zxyoi
	freopen("growing.in","r",stdin);
#endif
	Q=gi(),op=gi();
	inv[0]=inv[1]=1;for(int re i=2;i<=Q;++i)inv[i]=mul(inv[mod%i],mod-mod/i);
	n=1,LCT::init(1);Base[1]=dBase[0]=Random();
	while(Q--){
		int tp=gi();if(op)tp=(tp+pc)&1;
		switch(tp){
			case 0:Addnode();break;
			case 1:Query();break;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值