[做题记录]平衡树

记录一下平衡树的一些题目。
遇到了新的会更新。

(Ps:我不知道有些题目能不能算成平衡树的题目,因为在省选计划的[平衡树]模块里,又不想单独丢一个博客,就丢这里来好了,建议当做是数据结构做题记录的一部分来观看)。

[模板]平衡树

两版代码。
一版替罪羊。
一版无旋 \(treap\)
替罪羊还是好写。

替罪羊
// code by fhq_treap
#include<bits/stdc++.h>
#define ll long long
#define N 300005
#define alpha 0.7

inline ll read(){
    char C=getchar();
    ll A=0 , F=1;
    while(('0' > C || C > '9') && (C != '-')) C=getchar();
    if(C == '-') F=-1 , C=getchar();
    while('0' <= C && C <= '9') A=(A << 1)+(A << 3)+(C - 48) , C=getchar();
    return A*F;
}

template <typename T>
void write(T x)
{
    if(x < 0) {
        putchar('-');
        x = -x;
    }
    if(x > 9)
        write(x/10);
    putchar(x % 10 + '0');
    return;
}

int tot;

struct P{
	int l,r,v,sz,valid;
	bool del;
	inline void Newnode(int x){l = r = 0;sz = valid = 1;del = 0;v = x;}
}t[N << 2];

#define ls(o) t[o].l
#define rs(o) t[o].r

inline bool bad(int o){
	return std::max(1.0 * t[ls(o)].sz,1.0 * t[rs(o)].sz) > alpha * t[o].sz;
}

inline void up(int u){
	t[u].sz = t[ls(u)].sz + t[rs(u)].sz + !t[u].del;
	t[u].valid = t[ls(u)].valid + t[rs(u)].valid + !t[u].del;
}

inline void dfs(int u,std::vector<int> & v){
	if(!u)return ;
	dfs(ls(u),v);
	if(!t[u].del)v.push_back(u);
	dfs(rs(u),v);
}

inline int build(std::vector<int> &v,int l,int r){
	if(l >= r)return 0;
	#define mid ((l + r) >> 1)
	int u = v[mid];
	ls(u) = build(v,l,mid);
	rs(u) = build(v,mid + 1,r);
	up(u);
	return u;
}

inline void rebuild(int &u){
	std::vector<int>v;
	dfs(u,v);
	u = build(v,0,(int)v.size());
}

inline void insert(int x,int &u){
	if(!u){
		u = ++ tot;
		t[u].Newnode(x);
		return ;
	}
	t[u].sz ++;t[u].valid ++;
	if(x >= t[u].v)insert(x,rs(u));
	else
	insert(x,ls(u));
	if(bad(u))
	rebuild(u);
	return ;
}

inline int getrank(int u,int x){
	int ans = 1;
	while(u){
		if(t[u].v >= x)u = ls(u);
		else{
			ans += t[ls(u)].valid + !t[u].del;
			u = rs(u);
		}
	}
	return ans;
}

inline int findkth(int u,int x){
	while(u){
		if(!t[u].del && t[ls(u)].valid + 1 == x)
		return t[u].v;
		if(t[ls(u)].valid >= x)
		u = ls(u);
		else{
			x -= t[ls(u)].valid + !t[u].del;
			u = rs(u);
		}
	}
}

inline void Del(int u,int rk){
	if(!t[u].del && rk == t[ls(u)].valid + 1){
		t[u].del = 1;
		-- t[u].valid;
		return;
	}
	-- t[u].valid;
	if(rk <= t[ls(u)].valid + !t[u].del)
	Del(ls(u),rk);
	else
	Del(rs(u),rk - t[ls(u)].valid - !t[u].del);
//	up(u);
}

int n,rt;

int main(){
	scanf("%d",&n);
	rt = 0;
	while(n -- ){
		int opt,x;
		scanf("%d%d",&opt,&x);
		if(opt == 1)insert(x,rt);
		if(opt == 2)Del(rt,getrank(rt,x));
		if(opt == 3)std::cout<<getrank(rt,x)<<std::endl;
		if(opt == 4)std::cout<<findkth(rt,x)<<std::endl;
		if(opt == 5)std::cout<<findkth(rt,getrank(rt,x) - 1)<<std::endl;
		if(opt == 6)std::cout<<findkth(rt,getrank(rt,x + 1))<<std::endl;
	}
}
无旋
//rest : 126 days
#include<bits/stdc++.h>
#define ll long long
#define N 100005

int ch[N][2];
ll va[N],key[N];
int siz[N];

inline ll randdom(){return rand() << 15 | rand();}

int cnt;

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define v(x) va[x]
#define c(x) key[x]
#define s(x) siz[x]

inline int New(ll s){++cnt;s(cnt) = 1,v(cnt) = s,c(cnt) = randdom();return cnt;}

inline void up(int u){s(u) = 1 + s(ls(u)) + s(rs(u));}

inline void split(int u,ll k,int &x,int &y){//x,y分裂的两颗树,x小于key,y大于key 
	if(!u){x = y = 0;return;}
	if(v(u) <= k){x = u,split(rs(u),k,rs(u),y);}
	else{y = u,split(ls(u),k,x,ls(u));}
	up(u); 
}

inline int merge(int x,int y){//max \in x < min \in y small root 
	if(!x || !y)return x + y;
	if(c(x) < c(y))
	{rs(x) = merge(rs(x),y);up(x);return x;}
	else
	{ls(y) = merge(x,ls(y));up(y);return y;}
}

int root,x,y,z;

inline void insert(ll a){
	split(root,a,x,y);
	root = merge(merge(x,New(a)),y);
}

inline void del(ll a){
	split(root,a,x,z);
	split(x,a - 1,x,y);
	y = merge(ls(y),rs(y));
	root = merge(x,merge(y,z)); 
}

inline int find(ll a){
	split(root,a - 1,x,y);
	int ans = s(x) + 1;
	root = merge(x,y);
	return ans;
}

inline ll kth(int u,ll k){
	if(s(ls(u)) >= k)return kth(ls(u),k);
	if(s(ls(u)) + 1 == k)return u;
	return kth(rs(u),k - s(ls(u)) - 1);
} 

inline ll pre(ll a){
	split(root,a - 1,x,y);
	ll ans = v(kth(x,s(x)));
	root = merge(x,y);
	return ans;
}

inline ll nex(ll a){
	split(root,a,x,y);
	ll ans = v(kth(y,1));
	root = merge(x,y);
	return ans;
}

int n;

int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;++i){
		int opt;
		ll x;
		scanf("%d%lld",&opt,&x);
		if(opt == 1)insert(x);
		if(opt == 2)del(x);
		if(opt == 3)std::cout<<find(x)<<std::endl;
		if(opt == 4)std::cout<<v(kth(root,x))<<std::endl;
		if(opt == 5)std::cout<<pre(x)<<std::endl;
		if(opt == 6)std::cout<<nex(x)<<std::endl;
	}
}

[NOI2005] 维护数列

直接就按照平衡树维护序列的操作就好。
这题区间赋值,区间最大字段和要维护七八个量,着实太难写。
拉了个能看的sol。

[NOI2005] 维护数列
#include<bits/stdc++.h>
using namespace std;

int stk[500005],len; 

int inline read(){
	int num=0;bool neg=0;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') neg=1;c=getchar();}
	while(c>='0'&&c<='9') num=num*10+c-'0',c=getchar();
	return neg?-num:num; 
}

struct fhqtreap{
	int l,r,s,val,cov;bool lzy,cov_flg;
	int sum,maxqz,maxhz,maxzd;
}T[500005];int idx,rt;
int NewNode(int v){
	idx=stk[len--];T[idx].l=T[idx].r=T[idx].lzy=T[idx].cov=0;
	T[idx].s=1;
	T[idx].val=T[idx].sum=v;
	T[idx].maxqz=T[idx].maxhz=max(0,v);T[idx].maxzd=v;
	return idx;
}
void push_up(int x){
	if(!x) return;
	T[x].s=T[T[x].l].s+T[T[x].r].s+1;
	T[x].sum=T[T[x].l].sum+T[T[x].r].sum+T[x].val;
	T[x].maxqz=max(max(T[T[x].l].maxqz,T[T[x].l].sum+T[x].val+T[T[x].r].maxqz),0);
	T[x].maxhz=max(max(T[T[x].r].maxhz,T[T[x].r].sum+T[x].val+T[T[x].l].maxhz),0);
	T[x].maxzd=max(T[x].val,T[x].val+T[T[x].l].maxhz+T[T[x].r].maxqz);
	if(T[x].l) T[x].maxzd=max(T[x].maxzd,T[T[x].l].maxzd);
	if(T[x].r) T[x].maxzd=max(T[x].maxzd,T[T[x].r].maxzd);
}
void Reverse(int x){
	if(!x) return;
	swap(T[x].l,T[x].r);
	swap(T[x].maxhz,T[x].maxqz);
	T[x].lzy^=1;
}
void Cover(int x,int ci){
	T[x].val=T[x].cov=ci;T[x].sum=T[x].s*ci;
	T[x].maxqz=T[x].maxhz=max(0,T[x].sum);
	T[x].maxzd=max(ci,T[x].sum);
	T[x].cov_flg=1;
}
void push_down(int x){
	if(!x) return;
	if(T[x].lzy){
		if(T[x].l) Reverse(T[x].l);
		if(T[x].r) Reverse(T[x].r);
		T[x].lzy=0;
	}
	if(T[x].cov_flg){
		if(T[x].l) Cover(T[x].l,T[x].cov);
		if(T[x].r) Cover(T[x].r,T[x].cov);
		T[x].cov=T[x].cov_flg=0;
	}
}
void Del(int x){
	if(!x) return;
	stk[++len]=x;
	if(T[x].l) Del(T[x].l);if(T[x].r) Del(T[x].r);
}
void Split(int x,int &L,int &R,int K){
	if(x) push_down(x);
	if(!x){L=R=0;return;}
	if(T[T[x].l].s+1<=K) L=x,Split(T[x].r,T[L].r,R,K-T[T[x].l].s-1);
	else				 R=x,Split(T[x].l,L,T[R].l,K);
	push_up(x);
}
void Merge(int x,int y,int &M){
	if(!x||!y){M=x+y;return;}
	if(90000008%(T[x].s+T[y].s)<T[x].s)
		 push_down(x),M=x,Merge(T[x].r,y,T[M].r),push_up(x);
	else push_down(y),M=y,Merge(x,T[y].l,T[M].l),push_up(y);
}

int N,M;
int A[500005];
int Build(int l,int r){
	if(l==r){
		return NewNode(A[l]);
	}
	int x,mid=(l+r)>>1;
	Merge(Build(l,mid),Build(mid+1,r),x);
	return x;
}

char opt[10];
int main(){
	N=read(),M=read();
	for(int i=1;i<=500000;++i) stk[++len]=i;
	for(int i=1;i<=N;++i) A[i]=read();
	Merge(rt,Build(1,N),rt);
	while(M--){
		scanf("%s",opt);
		if(opt[0]=='I'){
			int pos=read(),tot=read();
			int x,y;
			Split(rt,x,y,pos);
			for(int i=1;i<=tot;++i) A[i]=read();
			Merge(x,Build(1,tot),x);
			Merge(x,y,rt);
		}
		else if(opt[0]=='D'){
			int pos=read(),tot=read();
			int x,y,z;
			Split(rt,x,y,pos-1);
			Split(y,y,z,tot);
			Del(y);
			Merge(x,z,rt);
		}
		else if(opt[0]=='M'&&opt[2]=='K'){
			int pos=read(),tot=read(),ci=read();
			int x,y,z;
			Split(rt,x,y,pos-1);
			Split(y,y,z,tot);
			Cover(y,ci);
			Merge(x,y,x);
			Merge(x,z,rt);
		}
		else if(opt[0]=='R'){
			int pos=read(),tot=read();
			int x,y,z;
			Split(rt,x,y,pos-1);
			Split(y,y,z,tot);
			Reverse(y);
			Merge(x,y,y);
			Merge(y,z,rt);
		}
		else if(opt[0]=='G'){
			int pos=read(),tot=read();
			int x,y,z;
			Split(rt,x,y,pos-1);
			Split(y,y,z,tot);
			printf("%d\n",T[y].sum);
			Merge(x,y,y);
			Merge(y,z,rt);
		}
		else if(opt[0]=='M'&&opt[2]=='X'){
			printf("%d\n",T[rt].maxzd);
		}
	}
	
	return 0;
}

[JSOI2008]火星人

依旧是维护序列问题。
不同的是我们要维护的是子树\(Hash\)权值和,这个很好维护,而且只有插入删除,我们选择这个替罪羊树。
查询的时候就直接二分查询即可。
比较好写,但是还是差点写自闭了。

[JSOI2008]火星人]
#include<bits/stdc++.h>
#define ll long long 
#define N 100005

double alpha = 0.7;

int n;

char si[N + 10];

int bit = 7;

unsigned ll F[N + 10];
unsigned ll hash[N + 10];

int cnt;
int val[N + 10];
int ch[N + 10][2];
int v[N + 10],siz[N + 10];

#define h(x) hash[x]
#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define s(x) siz[x]
#define v(x) v[x]

inline int New(int si){++cnt;int x = cnt;h(x) = si;s(x) = 1;v(x) = si;}

int root;

inline void up(int u){
	s(u) = s(ls(u)) + s(rs(u)) + 1;
	h(u) = h(ls(u)) + v(u) * F[s(ls(u))] + h(rs(u)) * F[s(ls(u)) + 1];
} 

inline bool bad(int u){return std::max(s(ls(u)),s(rs(u))) > alpha * s(u);}

inline void dfs(int u,std::vector<int> &v){
	if(!u)return ;
	dfs(ls(u),v);
	v.push_back(u);
	dfs(rs(u),v);
}

inline int build(std::vector<int> &v,int l,int r){
	if(l >= r)return 0;
	#define mid ((l + r) >> 1)
	int u = v[mid];
	ls(u) = build(v,l,mid);
	rs(u) = build(v,mid + 1,r);
	up(u);
	return u;
}

inline void rebuild(int &u){
	std::vector<int>v;
	dfs(u,v);
	u = build(v,0,(int)v.size()); 
}

inline void insert(int &u,int x,int si){//在x位置插入一个s字符 
//	std::cout<<u<<" "<<ls(u)<<" "<<s(ls(u))<<" "<<rs(u)<<" "<<s(rs(u))<<" "<<x<<" "<<si<<std::endl; 
	if(!u){
		New(si);
		u = cnt;
		return ;
	}
	if(bad(u))
	rebuild(u);	
	if(x <= s(ls(u)))
	insert(ls(u),x,si);	
	else
	insert(rs(u),x - s(ls(u)) - 1,si);
	up(u);
}

inline void change(int u,int x,int si){
//	std::cout<<u<<" "<<ls(u)<<" "<<s(ls(u))<<" "<<x<<" "<<si<<std::endl;
	if(s(ls(u)) + 1 == x)
	v(u) = si;
	else if(x <= s(ls(u)))change(ls(u),x,si);
	else change(rs(u),x - s(ls(u)) - 1,si);
	up(u);
}

inline unsigned ll find(int u,int l,int r,int tl,int tr){//...tl...u...tr... 
	unsigned ll res = 0;
	int M = l + s(ls(u));
	if(tl <= l && r <= tr){return h(u);}
	if(tl <= M - 1 && M - 1 >= l)
	res = find(ls(u),l,M - 1,tl,tr);
	if(tl <= M && M <= tr)//u点
	res = res + v(u) * F[M - std::max(tl,l)];
	if(M + 1 <= tr && M + 1 <= r)
	res = res + (M + 1 - std::max(tl,l) > 0 ? F[M + 1 - std::max(tl,l)] : 1) * find(rs(u),M + 1,r,tl,tr);
//	std::cout<<u<<" "<<l<<" "<<r<<" "<<tl<<" "<<tr<<" "<<M<<" "<<res<<" "<<std::endl;			
	return res;
}

inline void Dfs(int u){
	if(!u)return;
	Dfs(ls(u));
//	std::cout<<u<<" "<<ls(u)<<" "<<rs(u)<<" "<<v(u)<<" "<<h(u)<<std::endl;
	Dfs(rs(u));
}

int len = 0;

inline int solve(int x,int y){
	int l = 0,r = std::min(len - x + 1,len - y + 1);
	int ans = 0;
	#define mid ((l + r) >> 1)
	while(l <= r){
		if(find(root,1,len,x,x + mid - 1) == find(root,1,len,y,y + mid - 1))
		ans = mid,l = mid + 1;
		else
		r = mid - 1;
	}
	return ans;
}

int main(){
//	freopen("prefix.in","r",stdin);
//	freopen("prefix.out","w",stdout); 
	scanf("%s",si + 1);
	len = strlen(si + 1);
	F[0] = 1;
	for(int i = 1;i < N;++i)
	F[i] = F[i - 1] * bit; 	
	for(int i = 1;i <= len;++i)
	insert(root,i,si[i] - 'a');
	int n;
	scanf("%d",&n);
	int c = 0;
	while(n -- ){
		char a,b;
		while(a != 'Q' && a != 'R' && a != 'I')
		a = getchar();
		if(a == 'Q'){
			int x,y;
			scanf("%d%d",&x,&y);
//			std::cout<<x<<" "<<y<<std::endl;	
//			std::cout<<si + 268<<std::endl;
//			std::cout<<si + 446<<std::endl;				
//			//			std::cout<<find(root,1,s(root),x,y)<<std::endl;
			std::cout<<solve(x,y)<<std::endl;
//			std::cout<<find(root,1,len,446,450)<<std::endl;
		}
		if(a == 'R'){
			int x;
			scanf("%d",&x);
			while(!(b <= 'z' && b >= 'a'))
			b = getchar();
			si[x] = b;
			change(root,x,b - 'a');
		}
		if(a == 'I'){
			int x;
			scanf("%d",&x);
			while(!(b <= 'z' && b >= 'a'))
			b = getchar();
			insert(root,x,b - 'a');
			len ++ ;
		}
//		std::cout<<n<<std::endl;
		a = '.'; 
		b = '.';
	}	
}

[HNOI2011]括号修复 / [JSOI2011]括号序列

其实只有查询需要注意。

我们发现如果两两匹配,那么最后只剩\())))(((((\)这样的类型。

那么我们需要更改的值为 \(\lceil\frac{pre_{max}}{2}\rceil + \lceil\frac{|las_{min}\ |}{2}\rceil\)

至于区间赋值,翻转这些就比较平凡了。

这类有翻转和赋值的操作的维护序列题,建议使用\(fhq_treap\)因为其不需要思考如果进行区间操作,直接分裂即可。

[HNOI2011]括号修复 / [JSOI2011]括号序列]
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
using namespace std;
int n,m;
int root;
struct node
{
	int ls,rs,pmax,pmin,smax,smin,val,rnd,sum,siz;
	int retag;
	bool swtag;
	bool intag;
}tre[100005];
int cnt;
int newnode(int w)
{
	cnt++;
	tre[cnt].ls=tre[cnt].rs=0;
	tre[cnt].rnd=rand();
	tre[cnt].intag=tre[cnt].swtag=false;
	tre[cnt].val=w;
	tre[cnt].pmax=tre[cnt].smax=(w>0?w:0);
	tre[cnt].smin=tre[cnt].pmin=(w>0?0:w);
	tre[cnt].retag=0;
	tre[cnt].sum=w;
	tre[cnt].siz=1;
	return cnt;
}
void pushup(int x)
{
	int ls=tre[x].ls,rs=tre[x].rs;
	tre[x].pmax=max(tre[ls].pmax,tre[ls].sum+tre[x].val+tre[rs].pmax);
	tre[x].pmin=min(tre[ls].pmin,tre[ls].sum+tre[x].val+tre[rs].pmin);
	tre[x].smax=max(tre[rs].smax,tre[rs].sum+tre[x].val+tre[ls].smax);
	tre[x].smin=min(tre[rs].smin,tre[rs].sum+tre[x].val+tre[ls].smin);
	tre[x].siz=tre[ls].siz+tre[rs].siz+1;
    tre[x].sum=tre[ls].sum+tre[rs].sum+tre[x].val;
}
void pushdown(int x)
{
	int ls=tre[x].ls,rs=tre[x].rs;
	if(tre[x].retag!=0)
	{
		tre[ls].intag=tre[rs].intag=false;
		tre[ls].swtag=tre[rs].swtag=false;
		tre[ls].retag=tre[rs].retag=tre[x].retag;
		tre[ls].sum=tre[x].retag*tre[ls].siz;
		tre[rs].sum=tre[x].retag*tre[rs].siz;
		tre[ls].val=tre[rs].val=tre[x].retag;
		tre[ls].pmax=tre[ls].smax=(tre[ls].retag>0?tre[ls].retag*tre[ls].siz:0);
		tre[rs].pmax=tre[rs].smax=(tre[rs].retag>0?tre[rs].retag*tre[rs].siz:0);
		tre[ls].pmin=tre[ls].smin=(tre[ls].retag>0?0:tre[ls].retag*tre[ls].siz);
		tre[rs].pmin=tre[rs].smin=(tre[rs].retag>0?0:tre[rs].retag*tre[rs].siz);
		tre[x].retag=0;
	}
	if(tre[x].swtag)
	{
		tre[ls].swtag^=tre[x].swtag;
		tre[rs].swtag^=tre[x].swtag;
		swap(tre[ls].pmax,tre[ls].smax);
		swap(tre[ls].pmin,tre[ls].smin);
		swap(tre[x].ls,tre[x].rs);
		swap(ls,rs);
		swap(tre[ls].pmax,tre[ls].smax);
		swap(tre[ls].pmin,tre[ls].smin);
		tre[x].swtag=false;
	}
	if(tre[x].intag)
	{
		tre[ls].intag^=tre[x].intag;
		tre[rs].intag^=tre[x].intag;
		tre[ls].sum=-tre[ls].sum;
		tre[rs].sum=-tre[rs].sum;
		tre[ls].val=-tre[ls].val;
		tre[rs].val=-tre[rs].val;
		swap(tre[ls].pmax,tre[ls].pmin);
		tre[ls].pmax=-tre[ls].pmax;
		tre[ls].pmin=-tre[ls].pmin;
		swap(tre[ls].smax,tre[ls].smin);
		tre[ls].smax=-tre[ls].smax;
		tre[ls].smin=-tre[ls].smin;
		swap(tre[rs].pmax,tre[rs].pmin);
		tre[rs].pmax=-tre[rs].pmax;
		tre[rs].pmin=-tre[rs].pmin;
		swap(tre[rs].smax,tre[rs].smin);
		tre[rs].smax=-tre[rs].smax;
		tre[rs].smin=-tre[rs].smin;
		tre[x].intag=false;
	}
}
int merge(int x,int y)
{
	if(!x||!y) return x^y;
	int res=0;
	if(tre[x].rnd<tre[y].rnd)
	{
		res=x;
		pushdown(res);
		tre[x].rs=merge(tre[x].rs,y);
	}
	else
	{
		res=y;
		pushdown(res);
		tre[y].ls=merge(x,tre[y].ls);
	}
	pushup(res);
	return res;
}
void splitrk(int rt,int &lrt,int &rrt,int k)
{
	if(rt==0)
	{
		lrt=rrt=0;return;
	}
	pushdown(rt);
	int qwq=tre[tre[rt].ls].siz+1;
	if(qwq<=k)
	{
		lrt=rt;
		splitrk(tre[rt].rs,tre[lrt].rs,rrt,k-qwq); 
	}
	else
	{
		rrt=rt;
		splitrk(tre[rt].ls,lrt,tre[rrt].ls,k);
	}
	pushup(rt);
}
void ins(int w)
{
	root=merge(root,newnode(w));
}
int main()
{
	srand(114514);
	scanf("%d%d",&n,&m);
	string tmp;
	cin>>tmp;
	for(int i=1;i<=n;i++)
	{
		ins((tmp[i-1]==')'?1:-1));
	}
	for(int i=1;i<=m;i++)
	{
		string opt;
		int x,y;
		cin>>opt;
		scanf("%d%d",&x,&y);
		int rx,ry,rz;
		splitrk(root,rx,rz,y);
		splitrk(rx,rx,ry,x-1);
		if(opt[0]=='R')
		{
			string qwq;
			cin>>qwq;
			int val=(qwq[0]==')'?1:-1);
			tre[ry].intag=false;
			tre[ry].swtag=false;
			tre[ry].retag=val;
			tre[ry].sum=val*tre[ry].siz;
			tre[ry].val=val;
			tre[ry].pmax=tre[ry].smax=(tre[ry].retag>0?tre[ry].retag*tre[ry].siz:0);
			tre[ry].pmin=tre[ry].smin=(tre[ry].retag>0?0:tre[ry].retag*tre[ry].siz);
		}
		if(opt[0]=='I')
		{ 
			tre[ry].intag^=1;
			tre[ry].sum=-tre[ry].sum;
			tre[ry].val=-tre[ry].val;
			swap(tre[ry].pmax,tre[ry].pmin);
			tre[ry].pmax=-tre[ry].pmax;
			tre[ry].pmin=-tre[ry].pmin;
			swap(tre[ry].smax,tre[ry].smin);
			tre[ry].smax=-tre[ry].smax;
			tre[ry].smin=-tre[ry].smin;
		}
		if(opt[0]=='S')
		{
			tre[ry].swtag^=1;
			swap(tre[ry].pmax,tre[ry].smax);
			swap(tre[ry].pmin,tre[ry].smin);
		}
		if(opt[0]=='Q')
		{
			printf("%d\n",(tre[ry].pmax+1)/2+(-tre[ry].smin+1)/2);
		}
		root=merge(rx,merge(ry,rz));
	}
	return 0;
}

[POI2015]LOG

考虑有如下结论:
如果小于\(s\)的和设为\(sum\),大于\(s\)的有\(x\)个:
\((c - x) * s \leq sum\)时充分必要。

建议观看该题解

所以直接使用两颗\(BIT\)解决。

[POI2015]LOG
#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
#define lowbit(x) (x & -x)

struct Q{
	ll opt,x,y;
}q[1000005];

ll num[1000005],n,l;

ll tree1[1000005],tree2[1000005];

ll nnum[1000005];

void add(ll now,ll p,ll opt){
	if(opt == 1){
		for(int i = now;i <= l;i += lowbit(i))
			tree1[i] += p;
	}
	if(opt == 2){
		for(int i = now;i <= l;i += lowbit(i))
			tree2[i] += p;
	}	
}

ll query(ll now,ll opt){
	ll ans = 0;
	if(opt == 1){
		for(int i = now;i;i -= lowbit(i))
		ans += tree1[i];
		return ans;
	}
	if(opt == 2){
		for(int i = now;i;i -= lowbit(i))
		ans += tree2[i];
		return ans;
	}
}

ll m;

int main(){
	scanf("%lld%lld",&m,&n);
	for(int i = 1;i <= n;++i){
		char a[10];
		scanf("%s",a + 1);
		scanf("%lld%lld",&q[i].x,&q[i].y);
		if(a[1] == 'U')
			q[i].opt = 1;
		else 
			q[i].opt = 2;
		num[++num[0]] = q[i].y;
	}
	std::sort(num + 1,num + 1 + n);
	l = std::unique(num + 1,num + 1 + n) - num - 1;
	for(int i = 1;i <= n;++i){
		if(q[i].opt == 1){
			//std::cout<<q[i].y<<" "<<std::lower_bound(num + 1,num + l + 1,q[i].y) - num<<std::endl;
			add(std::lower_bound(num + 1,num + l + 1,q[i].y) - num,1,1);
			add(std::lower_bound(num + 1,num + l + 1,q[i].y) - num,q[i].y,2);
			if(nnum[q[i].x]){
			add(std::lower_bound(num + 1,num + l + 1,nnum[q[i].x]) - num,-1,1);
			add(std::lower_bound(num + 1,num + l + 1,nnum[q[i].x]) - num,-nnum[q[i].x],2);			
			}		
			nnum[q[i].x] = q[i].y;
		}
		if(q[i].opt == 2){
			ll s = query(l,1) - query(std::lower_bound(num + 1,num + l + 1,q[i].y) - num - 1,1);
			ll sum = query(std::lower_bound(num + 1,num + l + 1,q[i].y) - num - 1,2);
			//std::cout<<s<<" "<<sum<<std::endl;
			if(sum >= (q[i].x - s) * q[i].y)
			puts("TAK");
			else
			puts("NIE");
		}
	}
}
/*
3 8
U 1 5
U 2 7
Z 2 6
U 3 1
Z 2 6
U 2 2
Z 2 6
Z 2 1
*/

[Ynoi2013] 大学

经典结论:考虑每次操作每个数至少减半,所以直接暴力操作即可。

我们要做的就是快速维护 \([l,r]\)中为\(d\)的倍数的数。

直接暴力存下来共有\(nln\)个。

然后考虑并查集优化暴力过程。

\(O(nln(n)\alpha(n) + n log(a) log(n))\)

不想卡常,所以写了个vector版。

[Ynoi2013] 大学
#include<bits/stdc++.h>
#define ll long long 
#define N 500005

std::vector<int>Q[N + 100];
std::vector<int>pre[N + 100];

int n,m;

int a[N + 100];

std::vector<int>s[N + 100];

ll T[N];

#define lowbit(x) (x & -x)

inline int find(int x,int u){
	return pre[x][u] == u ? u : pre[x][u] = find(x,pre[x][u]);
}

inline void merge(int x,int l,int r){
	pre[x][l] = r;
}

inline void add(int x,int p){
	for(int i = x;i <= n;i += lowbit(i))
	T[i] += p;
}

inline ll q(int x){
	ll ans = 0;
	for(int i = x;i;i -= lowbit(i))
	ans = ans + T[i];
	return ans;
}

inline void div(int x,int l,int r){
	int now = std::lower_bound(Q[x].begin(),Q[x].end(),l) - Q[x].begin();
	while(Q[x][now] <= r){
		if(a[Q[x][now]] % x == 0){
			ll to = a[Q[x][now]] / x;
			add(Q[x][now],to - a[Q[x][now]]);
			a[Q[x][now]] /= x;
		}
		if(a[Q[x][now]] % x != 0 && pre[x][now] == now)
		merge(x,now,now + 1);
		now = find(x,now + 1);
	}
}

int cnt = 2;

int ans[N];

int main(){
	for(int i = 1;i < N;++i)
	for(int j = 1;j * i < N;++j)
	s[i * j].push_back(i);
	scanf("%d%d",&n,&m);
	for(int i = 1; i<= n;++i){
		scanf("%d",&a[i]);		
		add(i,a[i]);
		for(int j = 0;j < s[a[i]].size();++j)
		Q[s[a[i]][j]].push_back(i);
	}
	for(int i = 1;i < N;++i)
	Q[i].push_back(n + 1);
	for(int i = 1;i < N;++i){
	pre[i].resize(Q[i].size() + 10);
	for(int j = 0;j < Q[i].size();++j)
	pre[i][j] = std::min((int)Q[i].size() - 1,j);		
	}
	while(m -- ){
		int opt = 0;
		scanf("%d",&opt);
		if(opt == 1){
			int x,l,r;
			scanf("%d%d%d",&l,&r,&x);
			int f = ans[cnt - 2];
			l ^= f,r ^= f,x ^= f;
			div(x,l,r);
		}
		if(opt == 2){
			int l,r;
			scanf("%d%d",&l,&r);
			std::cout<<(ans[++cnt] = q(r) - q(l - 1))<<std::endl; 
		}
	}
}

[Ynoi2015] 我回来了

思考一下,由于乘上了 \(r - l + 1\) 所以其实等同于 \(\sum_{i = l}^r f(i)\),\(f(i)\)为当前那个序列的初始值为\(i\)的亵渎次数。

我们设\(a_x\)为值为\(x\)的第一次出现的操作数为\(a_x\)

那么我们设\(t_{d,i} = \min_{l = (i - 1) * d + 1}^{r = id}a_i\)
那么我们设\(f_{d,i}\)为初始值为\(d\),亵渎次数为\(i\)的最少操作数。

所以有\(f_{d,i} = \max(f_{d,i - 1},t_{d,i})\)

可以把\(f_{d,i}\)相同的放在一起,并给他们BIT上加一。

因为一共有 \(nlog\)\(t_{d,i}\)

复杂度为\(O(nlog^2n + mlog)\)

[Ynoi2015] 我回来了
#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define M 1000005

int n,a[N],m,f[N][20],lg[N];

std::pair<int,int>q[M];

std::vector<int>vec[M];

ll T[N];

#define lowbit(x) (x & -x)

inline void add(int x){
	for(int i = x;i <= n;i += lowbit(i))
	T[i] ++ ;
}

inline int find(int x){
	int ans = 0;
	for(int i = x;i;i -= lowbit(i))
	ans += T[i];
	return ans;
}

inline int Q(int l,int r){
	int k = lg[r -l + 1];
	return std::min(f[l][k],f[r - (1 << k) + 1][k]); 
}

int main(){
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n;++i)
	a[i] = m + 1;
	for(int i = 1;i <= m;++i){
		int opt;
		scanf("%d",&opt);
		q[i].first = -1;
		if(opt == 1){
			int x;
			scanf("%d",&x);
			a[x] = std::min(a[x],i);
		}else
			scanf("%d%d",&q[i].first,&q[i].second);
	}
	for(int i = 2;i <= n;++i)
	lg[i] = lg[i >> 1] + 1;
	for(int i = 1;i <= n;++i)
	f[i][0] = a[i];//ST表 
	for(int j = 1;j <= 18;++j)
	for(int i = 1;i + (1ll << j) - 1 <= n;++i)
	f[i][j] = std::min(f[i][j - 1],f[i + (1 << j - 1)][j - 1]);
	for(int i = 1;i <= n;++i){
		add(i);
		int las = 0;
		for(int j = 1;j <= n;j += i)
		vec[las = std::max(las,Q(j,std::min(j + i - 1,n)))].push_back(i);
	}
	for(int i = 1;i <= m;++i){
		for(int j = 0;j < vec[i].size();++j)
		add(vec[i][j]); 
		if(~q[i].first)
		std::cout<<find(q[i].second) - find(q[i].first - 1)<<std::endl; 
	}
}
posted @ 2021-12-07 21:14  fhq_treap  阅读(56)  评论(0编辑  收藏  举报