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号