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;
}
posted @ 2023-02-04 14:46  Star_LIcsAy  阅读(33)  评论(0)    收藏  举报