[学习笔记]珂朵莉树(Old Drive Tree)

image

我们以如下问题介绍操作并尝试进行一个浅显的复杂度分析:

操作一:区间加
操作二:区间赋值
操作三:区间和
保证数据随机

操作:

珂朵莉树在处理时,将数组分成包含连续相同段的若干段,并用\(set/list\)维护,对区间\([l,r]\)操作时,暴力枚举区间内的段,并\(O(1)\)修改一个段。

复杂度:

我们令\(O(pred)\) 为找到区间内第一段的复杂度,并令\(d\)为表示区间内当前包含的段数,那么我们知道一次操作\(O(pred + d)\) (操作二与\(d\)无关)。
从段数分析,操作一使段数增加\(O(1)\) 个,操作二使总段数减少\(O(d - 1)\)

Lemma 1.

若进行了 \(m\) 次操作,令随机变量 \(d_i\) 为第\(i\)次操作的段数,则\(\mathbf{E}[\sum_{i = 1}^m d_i] = O(n + m)\) ,即给出了忽略查询第一段操作后,我们的复杂度为\(O(n + m)\)

Proof

令随机变量\(f_i\)表示第\(i\)次删除的基本段数,那么有 \(\mathbf{E}[d_i] = O(\mathbf{E}[f_i] + 3)\) ,我们知道\(\sum_{i = 1}^mf_i \to O(n + m)\)
因为初始有\(O(n)\) 段,一次只会最多增加一个,而删除的段数不超过所有段数和,通过期望的线性性可以得证,\(\mathbf{E}[\sum_{i = 1}^m d_i] = O(n + m)\) ,事实上,通过直觉,我们很容易感知到这个结论。

Lemma 2

\(p_j\) 表示了一个下标\(j\in{1,....,\ n}\) 的段的端点在经过\(m\)次操作二后的过程中始终为基本段的端点的概率,那么有\(\frac{1}{n}\sum_{i = 1}^n p_j = O(\frac{1}{m})\)

Proof

image
实际上等同于一个硬算的技巧。

Lemma 3

\(t\)为总基本段,那么\(m\)次操作后期望为\(O(\frac{n}{m} + log\min({m,n}))\).

Proof

其为结论二的推论,其实只要求出基本段的端点总数从创建到最后存在的概率即可。
image

首段查询的复杂度

我们使用\(set\)维护

那么\(O(pred) = O(logt)\)

可得其期望复杂度为\(O(nloglogn)\)

本文大部分来自珂朵莉树的复杂度分析。
其在原文中还分析了线性做法。

#include<bits/stdc++.h>
#define ll long long 
#define MOD7 ((ll)1e9 + 7)
#define mod9 ((ll)1e9 + 9)
#define N 100007
#define int ll

using std::set;
using std::vector;
using std::pair;

struct P{
	int l,r;
	mutable ll v;//该块的key
	P(int L,int R = -1,ll V = 0):l(L),r(R),v(V){}; 
};

bool operator < (P a,P b){return a.l < b.l;}

set<P>s;//全部的块

inline set<P>::iterator split(int pos){
	set<P>::iterator it = s.lower_bound(P(pos));
	if(it != s.end() && it -> l == pos)return it;//刚好是一个块 
	-- it;
	int L = it -> l,R = it -> r;
	ll V = it -> v;
	s.erase(it);
	s.insert(P(L,pos - 1,V));
	s.insert(P(pos,R,V));
	return s.lower_bound(P(pos));;
} 

inline void add(int l,int r,ll p = 1){
//	std::cout<<"case1:"<<l<<" "<<r<<" "<<" "<<p<<std::endl;			
	set<P>::iterator li = split(l),ri = split(r + 1);
	while(li != ri){
		li -> v += p;
//		std::cout<<"!"<<li -> l<<" "<<li -> r<<" "<<li -> v<<std::endl; 				
		++li;
	} 
}

inline void cover(int l,int r,int p){
	set<P>::iterator li = split(l),ri = split(r + 1);
	s.erase(li,ri);
	s.insert(P(l,r,p));	
}

inline ll rank(int l,int r,int k){
	set<pair<ll,int> >V;
	V.clear();
	set<P>::iterator li = split(l),ri = split(r + 1);
	while(li != ri){
		V.insert(pair<ll,int>(li -> v,li -> r - li -> l + 1));
		++li;
	}	
	set<pair<ll,int> >::iterator u = V.begin();
	while(u != V.end()){
		k -= u -> second;
		if(k <= 0)return u -> first; 
		++u;
	}
	return -1;
}

ll pow(ll a,ll b,ll mod){
   ll res = 1;
   ll ans = a % mod;
   while(b){
   	 if(b & 1) res = res * a % mod;
   	 a = a * a % mod;
   	 b >>= 1;
   }
   return res;
}

inline ll sum(int l,int r,int x,int mod){
//	std::cout<<"case4"<<" "<<l<<" "<<r<<" "<<x<<" "<<mod<<std::endl; 
	set<P>::iterator li = split(l),ri = split(r + 1);
	ll res = 0;
	while(li != ri){
//		std::cout<<"?"<<li -> l<<" "<<li -> r <<" "<<li -> v<<std::endl;
		res = (res + 1ll * (li -> r - li -> l + 1) * pow(li -> v,x,mod) % mod);
		res = (res >= mod) ? res - mod : res;
		++li;
	}
	return res; 
}

int n, m;
ll seed, vmax;

ll rnd()
{
	ll ret = seed;
	seed = (seed * 7 + 13) % MOD7;
	return ret;
}

ll a[N];

signed main(){
	scanf("%lld%lld%lld%lld",&n,&m,&seed,&vmax);
	for(int i = 1;i <= n;++i){
		a[i] = (rnd() % vmax) + 1;
		s.insert(P(i,i,a[i]));		
//		std::cout<<a[i]<<" ";
	}
	s.insert(P(n + 1,n + 1,0));
	while(m -- ){
		int op = (rnd() % 4) + 1;
		int l = (rnd() % n) + 1;
		int r = (rnd() % n) + 1;
		if(l > r)std::swap(l,r);
		if(op == 1)add(l,r,(rnd() % vmax) + 1);
		if(op == 2)cover(l,r,(rnd() % vmax) + 1);
		if(op == 3)std::cout<<rank(l,r,(rnd() % (r - l + 1)) + 1)<<std::endl;
		if(op == 4){int x = (rnd() % vmax) + 1;std::cout<<sum(l,r,x,(rnd() % vmax) + 1)<<std::endl;}
	}
}
原文地址:https://www.cnblogs.com/dixiao/p/15652130.html