2023/2/4 考试总结
- 
树形数据结构专场 也就是我的大型 GG 专场
T1.P4587 [FJOI2016]神秘数
- 
主席树/可持久化线段树; 
- 
考试的时候怕调不出来,不是很敢写,然后还有性质分析得不是很清楚…… 于是 GG 了,还炫没了许多时间。结果 \(AC\) 代码意外的短。
- 
前置性质 
 设当前数字集合 \(S\) 已经可以表示 \([1,pos]\) 里的所有数,\(pos+1\) 就是第一个不能被表示的数。当前有一些 \(a_i\) 需要插入其中。- 当 \(a_i> pos+1\) 时,显然,即使把 \(a_i\) 插入 \(S\),\(pos+1\) 这个数仍然不能被凑出来,所以 \(a_i\) 不会对 \(pos\) 产生贡献;
- 当 \(a_i\le pos+1\) 时,插入 \(a_i\) 后就能表示 \([1,pos+a_i]\) 里的所有数;
 
- 
因此,问题转化为,从小到大向 \(S\) 里插入序列 \([l,r]\) 区间里的数,然后下一次可以插入的数范围在当前集合对应 \([1,pos+1]\) 中,也就是说,需要对区间求和。 
 由于要求区间,考虑主席树。
- 
在值域建树,用主席树维护区间和。 
 对于每次询问,从值域 \([1,1]\) 开始求解。
AC code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}
const int N=1e5+10;
struct memr{
	int ls,rs;
	int sum;
}tr[N<<5];
int n,m,cnt=0;
int rt[N];
#define mid ((l+r)>>1)
void update(int &now,int p,int l,int r,int x){
	now=++cnt;
	tr[now].ls=tr[p].ls,tr[now].rs=tr[p].rs;
	tr[now].sum=tr[p].sum+x;
	if(l==r) return ;
	if(x<=mid) update(tr[now].ls,tr[p].ls,l,mid,x);
	else update(tr[now].rs,tr[p].rs,mid+1,r,x);
	return ;
}
int ask(int a,int b,int l,int r,int ql,int qr){
	if(tr[a].sum==tr[b].sum) return 0;
	if(ql<=l && r<=qr) return tr[b].sum-tr[a].sum;
	int ans=0;
	if(ql<=mid) ans+=ask(tr[a].ls,tr[b].ls,l,mid,ql,qr);
	if(mid<qr) ans+=ask(tr[a].rs,tr[b].rs,mid+1,r,ql,qr);
	return ans; 
}
int main(){
	n=read();
	int a;
	for(int i=1;i<=n;++i){
		a=read();
		update(rt[i],rt[i-1],1,1e9,a);
	}
	m=read();
	int l,r;
	while(m--){
		l=read(),r=read();
		int mx=0,pos=0;
		for(;;){
			int ans=ask(rt[l-1],rt[r],1,1e9,mx+1,pos+1);
			if(!ans) break;
			mx=pos+1;
			pos+=ans;
		}
		printf("%d\n",pos+1);
	}
	return 0;
} 
T2.P2497 [SDOI2012]基站建设
- 
凭什么 n≤1e5 O(n²) 的暴力只给 10 pts?凭什么??
- 
斜率优化 \(\mathtt{DP}\),但由于横坐标不具有单调性,所以需要结合其他数据结构实现。 
T3.P3215 [HNOI2011]括号修复 / [JSOI2011]括号序列
- 
长得像是平衡树板题;
- 
将 (视为 \(-1\),)视为 \(1\),需要维护区间从左开始的最大/小子序列和以及从右开始的最大/小子序列和。同时维护三个修改操作的标记以及区间和。
 最后答案统计为 \((lmax+1)/2+(|rmn|+1)/2\)。
- 
设计好存储的对象之后就是纯纯的板题了,但代码很长,很容易 \(l\) \(r\) 或者 \(min\) \(max\) 写错 对我就是因为类似的大小问题反复 WA 了十几发
AC code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}
const int N=1e5+10;
int n,q,cnt=0;
int rt,a,b,c;
struct memr{
	int ls,rs;
	int val,sum;
	int siz,dat;
	int ph,fl,iv;
	int lmx,lmn,rmx,rmn;
	#define ls(i) tr[i].ls
	#define rs(i) tr[i].rs
}tr[N<<1];
int _New(int v){
	++cnt;
	tr[cnt].val=tr[cnt].sum=v;
	tr[cnt].dat=rand();
	tr[cnt].fl=tr[cnt].iv=tr[cnt].ph=0;
	tr[cnt].siz=1;
	if(v>0) tr[cnt].lmx=tr[cnt].rmx=1;
	else tr[cnt].lmn=tr[cnt].rmn=-1;
	return cnt;
}
void update(int p){
	tr[p].siz=tr[ls(p)].siz+tr[rs(p)].siz+1;
	tr[p].sum=tr[ls(p)].sum+tr[rs(p)].sum+tr[p].val; 
	tr[p].lmx=max(tr[ls(p)].lmx,tr[ls(p)].sum+tr[p].val+tr[rs(p)].lmx);
	tr[p].lmn=min(tr[ls(p)].lmn,tr[ls(p)].sum+tr[p].val+tr[rs(p)].lmn);
	tr[p].rmx=max(tr[rs(p)].rmx,tr[rs(p)].sum+tr[p].val+tr[ls(p)].rmx);
	tr[p].rmn=min(tr[rs(p)].rmn,tr[rs(p)].sum+tr[p].val+tr[ls(p)].rmn);
	return ;
}
void cover(int p,int v){//推平 
	if(!p) return ;
	tr[p].val=tr[p].ph=v;
	tr[p].sum=v*tr[p].siz;
	tr[p].lmx=tr[p].rmx=max(tr[p].sum,0);
	tr[p].lmn=tr[p].rmn=min(tr[p].sum,0);
	return ;
}
void reverse(int p){//翻转 
	if(!p) return ;
	tr[p].fl^=1;
	swap(ls(p),rs(p));
	swap(tr[p].lmn,tr[p].rmn);
	swap(tr[p].lmx,tr[p].rmx);
	return ;
}
void Invert(int p){//取反 
	if(!p) return ;
	tr[p].val=-tr[p].val;
	tr[p].sum=-tr[p].sum;
	int lmx=tr[p].lmx,rmx=tr[p].rmx;
	int lmn=tr[p].lmn,rmn=tr[p].rmn;
	tr[p].lmx=-lmn,tr[p].lmn=-lmx;
	tr[p].rmx=-rmn,tr[p].rmn=-rmx;
	tr[p].iv^=1;
	tr[p].ph=-tr[p].ph;
	return ;
}
void pushdown(int p){
	if(!p) return ;
	if(tr[p].iv){
		Invert(ls(p));
		Invert(rs(p));
		tr[p].iv=0;
	}
	if(tr[p].ph){
		cover(ls(p),tr[p].ph);
		cover(rs(p),tr[p].ph);
		tr[p].ph=0;
	}
	if(tr[p].fl){
		reverse(ls(p));
		reverse(rs(p));
		tr[p].fl=0;
	}
	return ;
}
int merge(int x,int y){
	pushdown(x),pushdown(y);
	if(!x || !y) return x+y;
	if(tr[x].dat>tr[y].dat){
		rs(x)=merge(rs(x),y);
		update(x);
		return x;
	}
	ls(y)=merge(x,ls(y));
	update(y);
	return y;
}
void split(int p,int k,int &x,int &y){
	if(!p){
		x=y=0;
		return ;
	}
	pushdown(p);
	if(tr[ls(p)].siz<k){
		x=p;
		split(rs(p),k-tr[ls(p)].siz-1,rs(p),y);
	}
	else{
		y=p;
		split(ls(p),k,x,ls(p));
	}
	update(p);
	return ;
}
void insert(int ch){
	rt=merge(rt,_New(ch));
	return ;
}
void push(int l,int r,int v){
	split(rt,r,a,c);
	split(a,l-1,a,b);
	cover(b,v);
	rt=merge(merge(a,b),c);
	return ;
}
void rv(int l,int r){
	split(rt,r,a,c);
	split(a,l-1,a,b);
	reverse(b);
	rt=merge(merge(a,b),c);
	return ;
}
void Inv(int l,int r){
	split(rt,r,a,c);
	split(a,l-1,a,b);
	Invert(b);
	rt=merge(merge(a,b),c);
	return ;
}
void check(int l,int r){
	split(rt,r,a,c);
	split(a,l-1,a,b);
	int ans;
	int t=tr[b].lmx;
	ans=(t&1)?((t>>1)+1):(t>>1);
	t=abs(tr[b].rmn);
	ans+=(t&1)?((t>>1)+1):(t>>1);
	printf("%d\n",ans);
	rt=merge(merge(a,b),c);
	return ;
}
int main(){
	srand(time(0));
	n=read(),q=read();
	char ch;
	for(int i=1;i<=n;++i){
		cin>>ch;
		insert((ch==')')?1:-1);
	}
	string opt;
	int x,y;
	while(q--){
		cin>>opt;
		x=read(),y=read();
		if(opt=="Replace"){
			cin>>ch;
			push(x,y,(ch==')')?1:-1);
		}
		else if(opt=="Swap")
			rv(x,y);
		else if(opt=="Invert")
			Inv(x,y);
		else check(x,y);
	}
	return 0;
}
 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号