1 条题解
-
0
自动搬运
来自洛谷,原作者为

LG_kemeng
高考冲刺725搬运于
2025-08-24 22:08:43,当前版本为作者最后更新于2022-11-12 20:30:16,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
珂朵莉+线段树方法
前缀知识:线段树,珂朵莉树(ODT),双指针
首先,这题数字和颜色之间明显没有直接的联系,并且一个珂朵莉无法处理。所以我们可以用两个结构分别储存这两个变量。数字要求最小区间和,用线段树储存;颜色区间修改,用珂朵莉储存。
构造
颜色
首先考虑颜色:对于颜色题目只有一个操作,
assign(),区间平推。数字
然后考虑数字:建立的线段树要满足下列操作:区间最大、区间最小、区间求和。区间最大和区间最小用做初始化(可能有 的情况,初始化答案用区间最大/最小值)。区间求和是题目两问的解。
所以话说真的有人会在做珂朵莉骗分的时候还加一个线段树吗。实现
操作一
- 询问区间 , 中包含所有(一共 种)颜色的区间和的最小值。
先用珂朵莉,左右界为:
itr=split(r+1),itl=split(l)。左右指针初始为 。右指针 不断 ++,直到找到一个点使颜色 有 种,更新答案 (即返回当前线段树querysum(itl->r,it->l))。这个时候,考虑将左指针 ++,并更新 。最终返回 即可。#define add(x) (!cnt[x]++&&++tot) #define del(x) (!--cnt[x]&&--tot) inline int Q1(int l,int r)//第一种情况 { if(c==1)//若c=1,特判,直接取区间最小值就行 return query_min(1,n,1,l,r); memset(cnt,0,sizeof(cnt));//初始化 IT it_r=split(r+1),it_l=split(l),it=it_l; register int t,res=INF,tot=0; while(it!=it_r)//枚举右指针,当有c种颜色时,考虑挪动左指针 { add(it->color); while(tot==c) t=query_sum(1,n,1,it_l->r,it->l),res=min(res,t),del((it_l++)->color); it++; } return res==INF?-1:res; }注: 为颜色 出现次数。
add里面先判断,所以 ++ 在 后面。del里面先减,所以 -- 在 前面。线段树querysum时,左指针所在区间有尾无头,右指针所在区间有头无尾,所以是querysum(itl->r,it->l)。操作二
- 求 中没有重复颜色的最大区间和。
和上一问思路差不多,同样用双指针实现。不过这一问是不断调整左指针 ,保证右指针的颜色的出现次数为 1。注意特判:如果右指针所在区间的 不为 1,因为条件不符合,直接跳过该段,即将左指针移至右指针,右指针 ++,避开这个区间。
inline int Q2(int l,int r)//第二种情况 { memset(cnt,0,sizeof(cnt));//初始化 IT it_r=split(r+1),it_l=split(l),it=it_l; register int t,res=query_max(1,n,1,l,r); while(it!=it_r)//类似的思路,不断调整左指针,保证右指针的颜色的出现数量始终为1。所以要加一个特判,如果右指针的size不为1,直接跳过,也就是令左指针直接移动到右指针的位置,右指针++ { cnt[it->color]++; while(it!=it_l&&cnt[it->color]>1) cnt[(it_l++)->color]--; if(it!=it_l) t=query_sum(1,n,1,it_l->r,it->l),res=max(res,t); if(it->l!=it->r) while(it!=it_l) cnt[(it_l++)->color]--; it++; } return res; }注意:考虑最差情况, 初始赋值区间内单点最大值。
AC Code
#include<bits/stdc++.h> using namespace std; #define N 100000 #define INF 1e9 #define IT set<node>::iterator #define add(x) (!cnt[x]++&&++tot) #define del(x) (!--cnt[x]&&--tot) //----------------------------------------------分割线---------------------------------------------------------------------// int n,c,a[N+5],v[N+5],cnt[N+5]; //线段树部分 struct tree { int S,Mx,Mn; inline tree(int s=0,int mx=-INF,int mn=INF):S(s),Mx(mx),Mn(mn){} inline tree operator + (const tree& t) const//重载+,方便运算 { return tree(S+t.S,max(Mx,t.Mx),min(Mn,t.Mn)); } }t[N<<2]; inline void build(int l,int r,int rt)//建树 { if(l==r) { t[rt]=tree(v[l],v[l],v[l]); return ; } register int mid=l+r>>1; build(l,mid,rt<<1),build(mid+1,r,rt<<1|1); t[rt]=t[rt<<1]+t[rt<<1|1]; } inline int query_sum(int l,int r,int rt,int ql,int qr)//区间和 { if(ql<=l&&r<=qr) return t[rt].S; register int mid=l+r>>1; register int sum=0; if(ql<=mid) sum+=query_sum(l,mid,rt<<1,ql,qr); if(qr>mid) sum+=query_sum(mid+1,r,rt<<1|1,ql,qr); return sum; } inline int query_max(int l,int r,int rt,int ql,int qr)//区间最大 { if(ql<=l&&r<=qr) return t[rt].Mx; register int t,maxx=-INF,mid=l+r>>1; if(ql<=mid) maxx=max(maxx,query_max(l,mid,rt<<1,ql,qr)); if(qr>mid) maxx=max(maxx,query_max(mid+1,r,rt<<1|1,ql,qr)); return maxx; } inline int query_min(int l,int r,int rt,int ql,int qr)//区间最小 { if(ql<=l&&r<=qr) return t[rt].Mn; register int t,minn=INF,mid=l+r>>1; if(ql<=mid) minn=min(minn,query_min(l,mid,rt<<1,ql,qr)); if(qr>mid) minn=min(minn,query_min(mid+1,r,rt<<1|1,ql,qr)); return minn; } inline void Init(int x,int* s)//输入 { for(register int i=1;i<=x;i++) v[i]=s[i]; build(1,x,1); } inline void Update(int x,int y)//更新 { register int l=1,r=n,rt=1,mid; while(l!=r)//二分查找 { mid=l+r>>1; if(x<=mid) r=mid,rt<<=1; else l=mid+1,(rt<<=1)|=1; } t[rt]=tree(y,y,y); while(rt>>=1)//向上更新 t[rt]=t[rt<<1]+t[rt<<1|1]; } //----------------------------------------------分割线---------------------------------------------------------------------// //珂朵莉树部分 struct node { int l,r,color; inline node(int x=0,int y=0,int p=0):l(x),r(y),color(p){} inline bool operator < (const node& t) const { return l<t.l; } };set<node> S; inline IT split(int pos)//区间分割 { IT it=S.lower_bound(node(pos)); if(it!=S.end()&&it->l==pos) return it; it--; node tmp=*it; S.erase(it); S.insert(node(tmp.l,pos-1,tmp.color)); return S.insert(node(pos,tmp.r,tmp.color)).first; } inline void assign(int l,int r,int color)//区间平推 { IT it_r=split(r+1),it_l=split(l); S.erase(it_l,it_r); S.insert(node(l,r,color)); } inline void _Init(int x,int* s)//输入 { for(register int i=(s[0]=s[x+1]=-1,1),t=0;i<=x+2;++i) s[i]^s[i-1]&&(S.insert(node(t,i-1,s[i-1])),t=i); } //----------------------------------------------分割线---------------------------------------------------------------------// //问题求解部分 inline int Q1(int l,int r)//第一种情况 { if(c==1)//若c=1,特判,直接取区间最小值就行 return query_min(1,n,1,l,r); memset(cnt,0,sizeof(cnt));//初始化 IT it_r=split(r+1),it_l=split(l),it=it_l; register int t,res=INF,tot=0; while(it!=it_r)//枚举右指针,当有c种颜色时,考虑挪动左指针 { add(it->color); while(tot==c) t=query_sum(1,n,1,it_l->r,it->l),res=min(res,t),del((it_l++)->color); it++; } return res==INF?-1:res;//=if(res==INF) return -1;else return res; } inline int Q2(int l,int r)//第二种情况 { memset(cnt,0,sizeof(cnt));//初始化 IT it_r=split(r+1),it_l=split(l),it=it_l; register int t,res=query_max(1,n,1,l,r); while(it!=it_r)//类似的思路,不断调整左指针,保证右指针的颜色的出现数量始终为1。所以要加一个特判,如果右指针的size不为1,直接跳过,也就是令左指针直接移动到右指针的位置,右指针++ { cnt[it->color]++; while(it!=it_l&&cnt[it->color]>1) cnt[(it_l++)->color]--; if(it!=it_l) t=query_sum(1,n,1,it_l->r,it->l),res=max(res,t); if(it->l!=it->r) while(it!=it_l) cnt[(it_l++)->color]--; it++; } return res; } void read(int &x)//官方给的快读,不用白不用(bushi) { char ch; while(ch = getchar(), ch < '!'); x = ch - 48; while(ch = getchar(), ch > '!') x = (x << 3) + (x << 1) + ch - 48; } int main() { register int T,i,opt,l,r,col; read(n),read(T),read(c); for(i=1;i<=n;++i) read(a[i]); Init(n,a); for(i=1;i<=n;++i) read(a[i]); _Init(n,a); while(T--) { read(opt),read(l),read(r); if(opt==1) Update(l,r); if(opt==2) read(col),assign(l,r,col); if(opt==3) printf("%d\n",Q1(l,r)); if(opt==4) printf("%d\n",Q2(l,r)); } return 0; }
- 1
信息
- ID
- 4209
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者