正题
今天写线段树和树状数组写得很爽
第一题:括号匹配
也许就我的做法这么傻,考虑将左括号看成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);
}

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

被折叠的 条评论
为什么被折叠?



