首师大附中集训第十八天综合模测

本文分享了使用线段树和树状数组解决三道编程题的心得,包括括号匹配、种树和树句节狗提等问题。通过实际操作,详细介绍了如何运用这些数据结构优化算法,提高效率。

正题

      今天写线段树和树状数组写得很爽

      第一题:括号匹配

      也许就我的做法这么傻,考虑将左括号看成1,右括号看成-1,然后目的就是交换两个不同的括号,使得所有的前缀和>=0。

      每次贪心取第一个右括号和最后一个左括号交换即可,这个过程用set维护,用线段树维护前缀和。

      题解非常简单。

#include<set>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define ls now<<1
#define rs now<<1|1
using namespace std;

int n;
char s[100010];
int mmin[300010],lazy[300010];
set<int> F,S;
set<int>::iterator a,b;

void change(int now,int x,int t,int l,int r){
	if(l==r) {mmin[now]=t;return ;}
	int mid=(l+r)/2;
	if(x<=mid) change(ls,x,t,l,mid);
	else change(rs,x,t,mid+1,r);
	mmin[now]=min(mmin[ls],mmin[rs]);
}

void pushdown(int now){
	mmin[ls]+=lazy[now],mmin[rs]+=lazy[now];
	lazy[ls]+=lazy[now],lazy[rs]+=lazy[now];
	lazy[now]=0;
}

void add(int now,int x,int y,int l,int r){
	if(x==l && y==r) {mmin[now]+=2;lazy[now]+=2;return ;}
	pushdown(now);
	int mid=(l+r)/2;
	if(y<=mid) add(ls,x,y,l,mid);
	else if(mid<x) add(rs,x,y,mid+1,r);
	else add(ls,x,mid,l,mid),add(rs,mid+1,y,mid+1,r);
	mmin[now]=min(mmin[ls],mmin[rs]);
}

int main(){
//	freopen("match010.in","r",stdin);
//	freopen("match.out","w",stdout);
	scanf("%d",&n);
	scanf("%s",s+1);
	int tot=0,x,y;
	for(int i=1;i<=n;i++) {
		if(s[i]=='(') tot++,F.insert(i);
		else tot--,S.insert(i);
		change(1,i,tot,1,n);
	}
	if(tot!=0) {printf("-1");return 0;}
	int ans=0;
	while(1){
		if(mmin[1]>=0) break;
		ans++;a=F.end();b=S.begin();a--;x=*a,y=*b;
		F.erase(a);S.erase(b);
		add(1,y,x-1,1,n);
	}
	printf("%d\n",ans);
}

      第二题:种树

      可以发现,根到一个节点的路径上,必须满足往左走的节点权值比当前点大,往右走的节点权值比当前点小。

      那么一个点满足条件当且仅当权值>=往右走的权值最大值,<=往左走的权值最小值,那么一个点就可能有两种计算进答案的方式,直接将这两种值附加进儿子上即可。

      考虑翻转的时候,相当于将这两种值翻转,那么我们维护多两个东西,一个是往右走的权值最小值,一个是往左走的权值最大值,然后更改线段树上的一个区间即可。

      比赛的时候想出了前两步,但是因为不会翻转被暴力怒踩30分。(暴力即正解?)

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define ls now<<1
#define rs now<<1|1
using namespace std;

const int N=100010;
int n,m,t;
int a[N],son[N][2],tot[N],h[N],dfn[N],top[N],f[N],c[N];
int M[2][N*3];
bool rev[N];

bool go(int x){
	int now=1;
	while(1){
		if(x==now) return true;
		if(rev[now]){
			rev[now]=false;
			rev[son[now][0]]^=true,rev[son[now][1]]^=true;
			swap(son[now][0],son[now][1]);
		}
		if(a[x]<a[now]) now=son[now][0];
		else if(a[now]<a[x]) now=son[now][1];
		else return false;
		if(now==0) return false;
	}
}

void subtask(){
	int opt,x,y;
	while(m--){
		scanf("%d %d",&opt,&x);
		if(opt==1) scanf("%d",&y),a[x]=y;
		else if(opt==2) rev[x]^=1;
		else printf(go(x)?"YES\n":"NO\n");
	}
	exit(0);
}

void dfs(int x){
	tot[x]=1;
	for(int i=0;i<=1;i++) if(son[x][i]){
		f[son[x][i]]=x;
		dfs(son[x][i]);
		tot[x]+=tot[son[x][i]];
		if(tot[son[x][i]]>tot[h[x]]) h[x]=son[x][i],c[x]=i;
	}
}

void dfs_2(int x,int tp){
	top[x]=tp;dfn[x]=++t;
	if(h[x]) dfs_2(h[x],tp);
	if(son[x][1-c[x]]) dfs_2(son[x][1-c[x]],son[x][1-c[x]]);
}

int dos(int ty,int x,int y){
	if(ty==0) return min(x,y);
	else return max(x,y);
}

void change(int now,int ty,int x,int t,int l,int r){
	if(l==r) {M[ty][now]=t;return ;}
	int mid=(l+r)/2;
	if(x<=mid) change(ls,ty,x,t,l,mid);
	else change(rs,ty,x,t,mid+1,r);
	M[ty][now]=dos(ty,M[ty][ls],M[ty][rs]);
}

int get_sum(int now,int ty,int x,int y,int l,int r){
	if(x==l && y==r) return M[ty][now];
	int mid=(l+r)/2;
	if(y<=mid) return get_sum(ls,ty,x,y,l,mid);
	else if(mid<x) return get_sum(rs,ty,x,y,mid+1,r);
	else return dos(ty,get_sum(ls,ty,x,mid,l,mid),get_sum(rs,ty,mid+1,y,mid+1,r));
}

bool solve(int x){
	int tp,xx=x;
	int ans[2];
	ans[0]=1e9,ans[1]=0;
	while(1){
		tp=top[x];
		if(dfn[tp]<dfn[x]){
			ans[0]=dos(0,ans[0],get_sum(1,0,dfn[tp],dfn[x]-1,1,n));
			ans[1]=dos(1,ans[1],get_sum(1,1,dfn[tp],dfn[x]-1,1,n));
		}
		x=f[tp];tp=top[x];
		if(x==0) break;
		ans[1-c[x]]=dos(1-c[x],ans[1-c[x]],a[x]);
	}
	return ans[1]<a[xx] && a[xx]<ans[0];
}

void exchange(int x){
	swap(son[x][0],son[x][1]);
	c[x]=1-c[x];
	if(h[x]){
		change(1,c[x],dfn[x],a[x],1,n);
		change(1,1-c[x],dfn[x],c[x]*1e9,1,n);
	}
	if(son[x][0]) exchange(son[x][0]);
	if(son[x][1]) exchange(son[x][1]);
}

int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d %d %d",&a[i],&son[i][0],&son[i][1]);
	subtask();
	dfs(1);dfs_2(1,1);
	for(int i=1;i<=n;i++) if(h[i]) change(1,c[i],dfn[i],a[i],1,n),change(1,1-c[i],dfn[i],c[i]*1e9,1,n);
	int opt,x,y;
	while(m--){
		scanf("%d %d",&opt,&x);
		if(opt==1){
			scanf("%d",&y);a[x]=y;
			if(h[x]) change(1,c[x],dfn[x],a[x],1,n);
		}
		else if(opt==3) printf(solve(x)?"YES\n":"NO\n");
		else exchange(x);
	}
}

      第三题:树句节狗提

      很容易就可以想到子树信息直接映射到dfs序上,第二个信息就是一个深度,二维偏序即可。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lowbit(x) x&(-x)
using namespace std;

const int N=2525011;
int n,Q,lim;
int a[N];
long long sum[N],ans[N];
struct edge{
	int y,next;
}s[N];
struct ques{
	int type,x,y,d,id;
	bool operator<(const ques q)const{
		return d!=q.d?d>q.d:type<q.type;
	}
}q[N<<1];
int first[N],len,tot;
int dis[N],l[N],r[N];

int read(int&x){
	x=0;
	char ch=getchar();
	while(ch<'0' || ch>'9') ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
}

void ins(int x,int y){
	s[++len]=(edge){y,first[x]};first[x]=len;
}

void dfs(int x,int fa){
	l[x]=++tot;q[tot]=(ques){1,l[x],a[x],dis[x],0};
	for(int i=first[x];i!=0;i=s[i].next) if(s[i].y!=fa) 
		dis[s[i].y]=dis[x]+1,dfs(s[i].y,x);
	r[x]=tot;
}

void add(int x,int t){
	while(x<=n){
		sum[x]+=t;
		x+=lowbit(x);
	}
}

long long get_sum(int x){
	long long tot=0;
	while(x){
		tot+=sum[x];
		x-=lowbit(x);
	}
	return tot;
}

void print(int q, long long* ans, int lim) {
	for(int i = 1; i <= q; ) {
		long long res = 0;
		for(int j = i; j <= min(q, i + lim - 1); j++) res ^= ans[j];
		i += lim;
		printf("%lld\n", res);
	}
	return ;
}

int main(){
	freopen("ds.in","r",stdin);
	freopen("ds.out","w",stdout);
	read(n);int x;
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=2;i<=n;i++) read(x),ins(x,i);
	dfs(1,0);read(Q);
	for(int i=1;i<=Q;i++){
		tot++;
		read(q[tot].x),read(q[tot].d);
		q[tot].d+=dis[q[tot].x],q[tot].y=r[q[tot].x],q[tot].x=l[q[tot].x];
		q[tot].type=2,q[tot].id=i;
	}
	sort(q+1,q+1+tot);
	for(int i=1;i<=tot;i++){
		if(q[i].type==1) add(q[i].x,q[i].y);
		else ans[q[i].id]=get_sum(q[i].y)-get_sum(q[i].x-1);
	}
	read(lim);
	print(Q,ans,lim);
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值