返回顶部

珂朵莉树

好文章

LUOGU
个人理解:珂朵莉树其实就是\(set\)大法,就是暴力,可以构造特殊数据卡掉,但是在随机数据的情况下,它是非常快的

struct ASD
{
	ll l,r;
	mutable ll val;//方便更改,指针是常量,必须加上mutable
	bool operator < (const ASD& A)const
	{
		return l<A.l;
	}
	ASD(ll a,ll b,ll c)
	{
		l=a;r=b;val=c;
	}
	ASD(ll a)
	{
		l=a;
	}
};
#define sit set<ASD>::iterator//懒人

分裂操作,将一个区间中的一个位置分裂成\([l,pos],[pos+1,r]\),是核心步骤,以后都是围绕他进行的

	set<ASD> s;
	sit split(ll pos)
	{
		sit it=s.lower_bound(ASD(pos));
		if(it!=s.end()&&(*it).l==pos)return it;
		it--;
		ll l=(*it).l,r=(*it).r,val=(*it).val;
		s.erase(it);
		s.insert(ASD(l,pos-1,val));//注意边界啊,以后要用到
		return s.insert(ASD(pos,r,val)).first;//first返回迭代器,second返回值
	}

区间推平,很暴力,一定要先找\(r+1\),再找&l&,否则位置会改变,\(RE\)

	void assign(ll l,ll r,ll val)
	{
		sit it2=split(r+1),it1=split(l);
		//删除区间[l,r+1)中所有的节点,注意左闭右开
		s.erase(it1,it2);
		s.insert(ASD(l,r,val));
	}	

同理,区间加

	void add(ll l,ll r,ll val)
	{
		sit it2=split(r+1),it1=split(l);
		for(sit it=it1;it!=it2;it++)
		{
			(*it).val+=val;
		}
	}

查找第\(k\)

	ll kth(ll l,ll r,ll k)
	{
		sit it2=split(r+1),it1=split(l);
		vector <pair<ll,ll>> a;
		a.clear();
		for(sit it=it1;it!=it2;it++)
		{
			a.push_back({(*it).val,(*it).r-(*it).l+1});
		}
		sort(a.begin(),a.end());//排个序就行了,真暴力
		for(ull i=0;i<a.size();i++)
		{
			k-=a[i].second;
			if(k<=0)return a[i].first;
		}
		// return 0;
	}
简单封一封
#define sit set<ASD>::iterator
struct ASD
{
	ll l,r;
	mutable ll val;
	bool operator < (const ASD& A)const
	{
		return l<A.l;
	}
	ASD(ll a,ll b,ll c)
	{
		l=a;r=b;val=c;
	}
	ASD(ll a)
	{
		l=a;
	}
};
struct ODT
{
	set<ASD> s;
	sit split(ll pos)
	{
		sit it=s.lower_bound(ASD(pos));
		if(it!=s.end()&&(*it).l==pos)return it;
		it--;
		ll l=(*it).l,r=(*it).r,val=(*it).val;
		s.erase(it);
		s.insert(ASD(l,pos-1,val));
		return s.insert(ASD(pos,r,val)).first;
	}
	void assign(ll l,ll r,ll val)
	{
		sit it2=split(r+1),it1=split(l);
		s.erase(it1,it2);
		s.insert(ASD(l,r,val));
	}	
	void add(ll l,ll r,ll val)
	{
		sit it2=split(r+1),it1=split(l);
		for(sit it=it1;it!=it2;it++)
		{
			(*it).val+=val;
		}
	}
	ll kth(ll l,ll r,ll k)
	{
		sit it2=split(r+1),it1=split(l);
		vector <pair<ll,ll>> a;
		a.clear();
		for(sit it=it1;it!=it2;it++)
		{
			a.push_back({(*it).val,(*it).r-(*it).l+1});
		}
		sort(a.begin(),a.end());
		for(ull i=0;i<a.size();i++)
		{
			k-=a[i].second;
			if(k<=0)return a[i].first;
		}
		// return 0;
	}
	ll cx(ll l ,ll r,ll x,ll mod)
	{
		sit it2=split(r+1),it1=split(l);
		ll ans=0;
		for(sit it=it1;it!=it2;it++)
		{
			ans=(ans+((*it).r-(*it).l+1)*qpow((*it).val,x,mod)%mod)%mod;
		}
		return ans;
	}
}odt;

例题

[模板CF896C]

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define ull unsigned long long
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
//#define int long long
#define pb push_back
#define pii pair<int,int>

using namespace std;
ll qpow(ll  a,ll b,ll mod)
{
	ll ans=1;
	a%=mod;//一定要有,CF不让用__int128
	while(b)
	{
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
#define sit set<ASD>::iterator
struct ASD
{
	ll l,r;
	mutable ll val;
	bool operator < (const ASD& A)const
	{
		return l<A.l;
	}
	ASD(ll a,ll b,ll c)
	{
		l=a;r=b;val=c;
	}
	ASD(ll a)
	{
		l=a;
	}
};
struct ODT
{
	set<ASD> s;
	sit split(ll pos)
	{
		sit it=s.lower_bound(ASD(pos));
		if(it!=s.end()&&(*it).l==pos)return it;
		it--;
		ll l=(*it).l,r=(*it).r,val=(*it).val;
		s.erase(it);
		s.insert(ASD(l,pos-1,val));
		return s.insert(ASD(pos,r,val)).first;
	}
	void assign(ll l,ll r,ll val)
	{
		sit it2=split(r+1),it1=split(l);
		s.erase(it1,it2);
		s.insert(ASD(l,r,val));
	}	
	void add(ll l,ll r,ll val)
	{
		sit it2=split(r+1),it1=split(l);
		for(sit it=it1;it!=it2;it++)
		{
			(*it).val+=val;
		}
	}
	ll kth(ll l,ll r,ll k)
	{
		sit it2=split(r+1),it1=split(l);
		vector <pair<ll,ll>> a;
		a.clear();
		for(sit it=it1;it!=it2;it++)
		{
			a.push_back({(*it).val,(*it).r-(*it).l+1});
		}
		sort(a.begin(),a.end());
		for(ull i=0;i<a.size();i++)
		{
			k-=a[i].second;
			if(k<=0)return a[i].first;
		}
		// return 0;
	}
	ll cx(ll l ,ll r,ll x,ll mod)
	{
		sit it2=split(r+1),it1=split(l);
		ll ans=0;
		for(sit it=it1;it!=it2;it++)
		{
			ans=(ans+((*it).r-(*it).l+1)*qpow((*it).val,x,mod)%mod)%mod;
		}
		return ans;
	}
}odt;
const int N = 1e5+5;
ll n,m,seed,vmax,a[N];
ll rnd()
{
    ll ret = seed;
    seed = (seed * 7 + 13)%1000000007;
    return ret;
}

int main ()
{
	speed();
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n>>m>>seed>>vmax;
	// cout<<n<<endl;
	for(int i=1;i<=n;i++)
	{
		a[i]=(rnd()%vmax)+1;
		odt.s.insert(ASD(i,i,a[i]));
	}
	// cout<<"******"<<endl;
	ll op,l,r,x,y;
	for(int i=1;i<=m;i++)
	{
		// cout<<i<<endl;
		op=(rnd()%4)+1;
		// cout<<op<<endl;
		l=(rnd()%n)+1;
		r=(rnd()%n)+1;
		if(l>r)swap(l,r);
		if(op==3)
		{
			x=rnd()%(r-l+1)+1;
			cout<<odt.kth(l,r,x)<<endl;
		}else
		{
			x=rnd()%vmax+1;
		}
		
		if(op==1)
		{
			// cout<<i<<endl;
			odt.add(l,r,x);
		}
		if(op==2)odt.assign(l,r,x);
		if(op==4)
		{
			y=rnd()%vmax+1;
			cout<<odt.cx(l,r,x,y)<<endl;
		}
		// cout<<"*******"<<endl;
	}
	return 0;
}

image

P9340 [JOISC 2023 Day3] Tourism

image

这题除了用回滚莫队以外,还可以用\(ODT\),具体如何用
我们发现一个查询区间内的共同\(LCA\)可以用\(ST\)表一次性处理出来,这样,我们发现多出的答案就是\(dep_{lca}\)最后答案还要加\(1\),即为\(dep_1\),然后考虑如何维护,考虑把询问的区间按\(R\)排序,按时间维护
考虑离线后按\(R\) 排序,以时间为值进行覆盖,则此时\(LCA\)内值\(>=L\)的边就一定会被统计。这时\(LCA\) 的子树外的边不会统计,但是这些边被被赋的值一定是当前最大的,直接减去即可。这是转化为了路径赋值 \((1,u)\),全局查询值在某个区间的数的个数。
但这样的时间复杂度依然很高。想想区间赋值,可以直接用 ODT。这里 ODT 的复杂度显然是可以保证的,但是每次查询暴力扫一遍 \(1\sim m\) 的话会炸,再用一棵 BIT 维护值域即可。

有更优的实现方式:参考 Leasier 的思路,发现是对每条重链前缀赋值,可以使用 vector 替代 ODT。
时间复杂度:\(\mathcal{O}((n+q)\log^2n)\),这里认为 \(n,m\) 同阶。
空间复杂度:\(\mathcal{O}(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define pb push_back
#define ull unsigned long long
#define pii pair<int,int>
using namespace std;
const int N =1e5+5;
vector <int> edge[N];
int n,m,Q,a[N],st[18][N],log_2[N],ans[N];;
struct BIT{
	int c[N];
	int lowbit(int x){return x&-x;};
	void add(int x,int y){assert(x);for(;x<=m;x+=x&-x)c[x]+=y;}
	int ask(int x){
		int res=0;
		for(;x;x-=x&-x)res+=c[x];
		return res;
	}
} T;
struct ASD
{
	int l,r;
	mutable ll val;
	bool operator < (const ASD& A)const
	{
		return l<A.l;
	}
	ASD(int l,int r,ll val):l(l),r(r),val(val){};
	ASD(int l):l(l){};
};
void add(int u,int v)
{
	edge[u].pb(v);edge[v].pb(u);
}
int sz[N],dfstot,dfn[N],rk[N],fa[N],top[N],dep[N],son[N];
void dfs1(int u,int f,int de)
{
	sz[u]=1;dep[u]=de;
	son[u]=0;
	int ms=0;fa[u]=f;
	for(auto to:edge[u])
	{
		if(to==f)continue;
		dfs1(to,u,de+1);
		sz[u]+=sz[to];
		if(ms<sz[to])
		{
			ms=sz[to];
			son[u]=to;
		}
	}
}

void dfs2(int u,int ans)
{
	dfn[u]=++dfstot;
	rk[dfstot]=u;top[u]=ans;
	// if(!son[u])return;
	if(son[u])dfs2(son[u],ans);
	// else return;
	for(auto to:edge[u])
	{
		if(to==fa[u]||to==son[u])continue;
		dfs2(to,to);
	}
}
int glca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	return v;
}
int qlca(int l,int r){
	int k=log_2[r-l+1];
	return glca(st[k][l],st[k][r-(1<<k)+1]);
}
#define sit set<ASD>::iterator
struct ODT
{
	set <ASD> s;
	sit split(int pos)
	{
		sit it=s.lower_bound(ASD(pos,0,0));
		if(it!=s.end()&&(*it).l==pos)return it;
		it--;
		int l=(*it).l,r=(*it).r;ll val=(*it).val;
		s.erase(it);
		s.insert(ASD(l,pos-1,val));
		return s.insert(ASD(pos,r,val)).first;
	} 
	void assign(int l,int r,int val)
	{
		sit it2=split(r+1),it1=split(l);
		for(auto it=it1;it!=it2;it++)T.add(m-(*it).val+1,-((*it).r-(*it).l+1));//更新树状数组,删掉数量为区间长度的点数
		s.erase(it1,it2);
		s.insert(ASD(l,r,val));
		T.add(m-val+1,r-l+1);//把新的加上,位置变了
	}
	void upd(int u,int tim)
	{
		while(u)
		{
			assign(dfn[top[u]],dfn[u],tim);//区间推平
			u=fa[top[u]];
		}
	}
}odt;
vector<int> vec[N];pii q[N];
int main()
{
	speed();
	freopen("in.in","r",stdin);
	freopen("out.out","w",stdout);
	cin>>n>>m>>Q;
	odt.s.insert(ASD(1,n,0));
	int  u,v;
	for(int i=1;i<n;i++)
	{
		cin>>u>>v;add(u,v);
	}
	for(int i=1;i<=m;i++)cin>>a[i],st[0][i]=a[i];
	dfs1(1,0,1);dfs2(1,1);
	for(int i=2;i<=m;i++)log_2[i]=log_2[i>>1]+1;
	for(int i=1;i<=17;i++)for(int j=1;j+(1<<i)-1<=m;j++)st[i][j]=glca(st[i-1][j],st[i-1][j+(1<<(i-1))]);
	for(int i=1;i<=Q;i++)cin>>q[i].first>>q[i].second,vec[q[i].second].pb(i),ans[i]=-dep[qlca(q[i].first,q[i].second)]+dep[1];
	for(int i=1;i<=m;i++){
		odt.upd(a[i],i);
		for(int j:vec[i])ans[j]+=T.ask(m-q[j].first+1);//这里相当于把区间倒过来了
	}
	for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
	return 0;
}
容易理解
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define pb push_back
#define ull unsigned long long
#define pii pair<int,int>
using namespace std;
const int N =1e5+5;
vector <int> edge[N];
int n,m,Q,a[N],st[18][N],log_2[N],ans[N];;
struct BIT{
	int c[N];
	int lowbit(int x){return x&-x;};
	void add(int x,int y)
	{
		while(x)
		{
			c[x]+=y;
			x-=lowbit(x);
		}
	}
	int ask(int x){
		int res=0;
		while(x<=m)
		{
			res+=c[x];
			x+=lowbit(x);
		}
		return res;
	}
} T;
struct ASD
{
	int l,r;
	mutable ll val;
	bool operator < (const ASD& A)const
	{
		return l<A.l;
	}
	ASD(int l,int r,ll val):l(l),r(r),val(val){};
	ASD(int l):l(l){};
};
void add(int u,int v)
{
	edge[u].pb(v);edge[v].pb(u);
}
int sz[N],dfstot,dfn[N],rk[N],fa[N],top[N],dep[N],son[N];
void dfs1(int u,int f,int de)
{
	sz[u]=1;dep[u]=de;
	son[u]=0;
	int ms=0;fa[u]=f;
	for(auto to:edge[u])
	{
		if(to==f)continue;
		dfs1(to,u,de+1);
		sz[u]+=sz[to];
		if(ms<sz[to])
		{
			ms=sz[to];
			son[u]=to;
		}
	}
}

void dfs2(int u,int ans)
{
	dfn[u]=++dfstot;
	rk[dfstot]=u;top[u]=ans;
	// if(!son[u])return;
	if(son[u])dfs2(son[u],ans);
	// else return;
	for(auto to:edge[u])
	{
		if(to==fa[u]||to==son[u])continue;
		dfs2(to,to);
	}
}
int glca(int u,int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=fa[top[u]];
	}
	if(dep[u]<dep[v])swap(u,v);
	return v;
}
int qlca(int l,int r){
	int k=log_2[r-l+1];
	return glca(st[k][l],st[k][r-(1<<k)+1]);
}
#define sit set<ASD>::iterator
struct ODT
{
	set <ASD> s;
	sit split(int pos)
	{
		sit it=s.lower_bound(ASD(pos,0,0));
		if(it!=s.end()&&(*it).l==pos)return it;
		it--;
		int l=(*it).l,r=(*it).r;ll val=(*it).val;
		s.erase(it);
		s.insert(ASD(l,pos-1,val));
		return s.insert(ASD(pos,r,val)).first;
	} 
	void assign(int l,int r,int val)
	{
		sit it2=split(r+1),it1=split(l);
		for(auto it=it1;it!=it2;it++)T.add((*it).val,-((*it).r-(*it).l+1));//更新树状数组,删掉数量为区间长度的点数
		s.erase(it1,it2);
		s.insert(ASD(l,r,val));
		T.add(val,r-l+1);//把新的加上,位置变了
	}
	void upd(int u,int tim)
	{
		while(u)
		{
			assign(dfn[top[u]],dfn[u],tim);//区间推平
			u=fa[top[u]];
		}
	}
}odt;
vector<int> vec[N];pii q[N];
int main()
{
	speed();
	// freopen("in.in","r",stdin);
	// freopen("out.out","w",stdout);
	cin>>n>>m>>Q;
	odt.s.insert(ASD(1,n,0));
	int  u,v;
	for(int i=1;i<n;i++)
	{
		cin>>u>>v;add(u,v);
	}
	for(int i=1;i<=m;i++)cin>>a[i],st[0][i]=a[i];
	dfs1(1,0,1);dfs2(1,1);
	for(int i=2;i<=m;i++)log_2[i]=log_2[i>>1]+1;
	for(int i=1;i<=17;i++)for(int j=1;j+(1<<i)-1<=m;j++)st[i][j]=glca(st[i-1][j],st[i-1][j+(1<<(i-1))]);
	for(int i=1;i<=Q;i++)cin>>q[i].first>>q[i].second,vec[q[i].second].pb(i),ans[i]=-dep[qlca(q[i].first,q[i].second)]+dep[1];
	// cout<<"&&&"<<qlca(1,2)<<endl;

	for(int i=1;i<=m;i++){
		odt.upd(a[i],i);
		for(int j:vec[i])ans[j]+=T.ask(q[j].first);//这里我把区间正过来了,但是树状数组需要改变lowbit加减方向
	}
	for(int i=1;i<=Q;i++)printf("%d\n",ans[i]);
	return 0;
}

update 8.14

用树链剖分将树转换为序列,然后询问按\(r\)排序,对\(a_{i-1}->a_i\)染色位\(i\),答案变为查询颜色为\(l+1,r\),用树状数组维护即可

点击查看代码
#include <bits/stdc++.h>
#define speed() ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define ll long long
#define pb push_back
#define ull unsigned long long
#define pii pair<int,int>
#define lid (rt<<1)
#define rid (rt<<1|1)
// #define endl '\n'
#define ts cout<<"**********"<<endl;
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar_unlocked();
	for(;ch<'0'||ch>'9';ch=getchar_unlocked())ch=='-'?f=-1:f=f;
	for(;ch>='0'&&ch<='9';ch=getchar_unlocked())x=(x<<3)+(x<<1)+(ch^48);
	return x*f;
}
inline void write(int x)
{
	return x<0?(putchar_unlocked('-'),write(-x),void(0)):(x==0?void(0):(write(x/10),putchar_unlocked((x%10)|48),void(0)));
}
const int N = 1e5+5;
int n,m,a[N],Q;
struct Query
{
	int l,r,id;
}q[N];
struct BIT
{
	int c[N];
	inline int lowbit(int x){return x&-x;}
	inline void add(int x,int v){while(x<=m){c[x]+=v;x+=lowbit(x);}}
	inline int query(int x){int ans=0;while(x){ans+=c[x];x-=lowbit(x);}return ans;}
}T;
int dfn[N],dep[N],son[N],fa[N],rk[N],top[N],dfstot,sz[N];
vector <int> edge[N];
void add(int u,int v){edge[u].pb(v);edge[v].pb(u);}
struct ODT
{
	struct ASD
	{
		int l,r;mutable int val;
		bool operator < (const ASD& A)const
		{
			return l<A.l;
		}
	};
	// #define sit set<ASD>::iterator
	set <ASD> s;
	inline auto split(int pos)
	{
		auto it=s.lower_bound(ASD{pos,0,0});
		if(it!=s.end()&&it->l==pos)return it;
		it--;
		int l=it->l,r=it->r,val=it->val;
		s.erase(it);
		s.insert({l,pos-1,val});
		return s.insert({pos,r,val}).first;
	}
	inline void update(int l,int r,int val)
	{
		auto it2=split(r+1),it1=split(l);
		for(auto it=it1;it!=it2;it++)
		{
			T.add(it->val,-(it->r-it->l+1));
		}
		s.erase(it1,it2);s.insert({l,r,val});
		T.add(val,r-l+1);		
	}
}odt;
struct Tree
{
	void dfs1(int u,int _f,int d)
	{
		fa[u]=_f;dep[u]=d;int ms=0;sz[u]=1;
		for(auto to:edge[u])
		{
			if(to==fa[u]||dep[to])continue;
			dfs1(to,u,d+1);
			sz[u]+=sz[to];
			if(sz[to]>ms)ms=sz[to],son[u]=to;
		}
	}
	void dfs2(int u,int ans)
	{
		dfn[u]=++dfstot;rk[dfstot]=u;top[u]=ans;
		if(son[u])dfs2(son[u],ans);
		for(auto to:edge[u])
		{
			if(to==son[u]||to==fa[u])continue;
			dfs2(to,to);
		}
	}
	void update(int l,int r,int col)
	{
		while(top[l]!=top[r])
		{
			if(dep[top[l]]<dep[top[r]])swap(l,r);
			odt.update(dfn[top[l]],dfn[l],col);
			l=fa[top[l]];
		}
		if(dep[l]>dep[r])swap(l,r);
		odt.update(dfn[l],dfn[r],col);
	}
}st;
vector <int> pos[N];int ans[N];
int main()
{
	// freopen("P9340_1.in","r",stdin);
	// freopen("out.out","w",stdout);
	n=read();m=read();Q=read();int u,v;
	odt.s.insert({1,n,1});T.add(1,n);
	for(int i=1;i<=n-1;i++)u=read(),v=read(),add(u,v);
	for(int i=1;i<=m;i++)a[i]=read();
	st.dfs1(1,0,1);st.dfs2(1,1);
	for(int i=1;i<=Q;i++)
	{
		q[i].l=read(),q[i].r=read(),q[i].id=i;
		if(q[i].l==q[i].r)ans[i]=1;
		else pos[q[i].r].pb(i);
	}
	for(int i=2;i<=m;i++)
	{
		st.update(a[i-1],a[i],i);
		for(auto id:pos[i])ans[id]=n-T.query(q[id].l);
	}
	for(int i=1;i<=Q;i++)write(ans[i]),putchar_unlocked('\n');
	return 0;
}
posted @ 2024-07-30 07:49  wlesq  阅读(12)  评论(0编辑  收藏  举报