20230621-Segment Tree 1
20230621
Segment Tree
在写线段树前想想:
1.每个区间需要记录哪些值?
2.需要哪些标记?
3.如何叠加标记(在原有标记的区间增加新的标记?)
4.如何对区间进行整体修改?
5.如何合并区间?
P1471 方差
题目大意
给定一个序列,要求支持:区间加,区间平均数,区间方差。
其中方差定义为:
Solution
很基础的线段树
维护平均数就只需要维护区间和和区间个数
再来考虑方差
先把
这样就可以在修改时利用区间和
所以我们只需要维护
在区间修改时
可以用同样的方法转化式子
我们就可以通过之前的
于是这道题就做完了
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn=1e5+10;
int n,m,op,x,y;
double a[maxn],v,sum,s;
struct node{
double sum,s,val,tag;
int l,r;
}tr[maxn*4];
void pushup(int rt){
tr[rt].sum=tr[rt<<1].sum+tr[rt<<1|1].sum;
tr[rt].s=tr[rt<<1].s+tr[rt<<1|1].s;
}
void build(int l,int r,int rt){
tr[rt].l=l;tr[rt].r=r;
if(l==r){
tr[rt].val=a[l];
tr[rt].sum=a[l];
tr[rt].s=a[l]*a[l];
tr[rt].tag=0;
return ;
}
build(lson);build(rson);
pushup(rt);
}
void pushdown(int rt){
double t=tr[rt].tag;
tr[rt].tag=0;
tr[rt<<1].tag+=t;
tr[rt<<1|1].tag+=t;
tr[rt<<1].s=tr[rt<<1].s+(tr[rt<<1].r-tr[rt<<1].l+1)*t*t+2*t*tr[rt<<1].sum;
tr[rt<<1|1].s=tr[rt<<1|1].s+(tr[rt<<1|1].r-tr[rt<<1|1].l+1)*t*t+2*t*tr[rt<<1|1].sum;
tr[rt<<1].sum+=(tr[rt<<1].r-tr[rt<<1].l+1)*t;
tr[rt<<1|1].sum+=(tr[rt<<1|1].r-tr[rt<<1|1].l+1)*t;
}
void update(int l,int r,int rt,int x,int y,double val){
if(x<=l&&y>=r){
tr[rt].tag+=val;
tr[rt].s=tr[rt].s+(r-l+1)*val*val+2*val*tr[rt].sum;
tr[rt].sum=tr[rt].sum+(r-l+1)*val;
return;
}
if(tr[rt].tag) pushdown(rt);
if(x<=mid) update(lson,x,y,val);
if(y>mid) update(rson,x,y,val);
pushup(rt);
}
void query(int l,int r,int rt,int x,int y,double &sum,double &s){
if(x<=l&&y>=r){
sum+=tr[rt].sum;
s+=tr[rt].s;
return ;
}
if(tr[rt].tag) pushdown(rt);
if(x<=mid) query(lson,x,y,sum,s);
if(y>mid) query(rson,x,y,sum,s);
}
int main(){
/*2023.6.21 hewanying P1471 方差 线段树*/
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lf",&a[i]);
build(1,n,1);
for(int i=1;i<=m;i++){
scanf("%d",&op);
sum=0,s=0;
if(op==1){
scanf("%d%d%lf",&x,&y,&v);
update(1,n,1,x,y,v);
}
if(op==2){
scanf("%d%d",&x,&y);
query(1,n,1,x,y,sum,s);
printf("%.4lf\n",(double)sum/(y-x+1));
}
if(op==3){
scanf("%d%d",&x,&y);
query(1,n,1,x,y,sum,s);
printf("%.4lf\n",(double)((double)(s-sum*2*(sum/(y-x+1)))/(y-x+1)+(double)(sum/(y-x+1))*(sum/(y-x+1))));
}
}
return 0;
}
P1558 色板游戏
题目大意
给定一个区间,要求维护下列操作:把一个区间染上一个颜色;询问一个区间
的颜色个数,n, m ≤ 105,颜色数不多于 30。
Solution
发现颜色最多是30
所以开一个线段树暴力枚举就行了
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn=1e5+10;
int n,m,q,ans=0;
struct node{
int tag,num;
}tr[maxn*4][35];
bool vis[35];
void pushup(int rt){
for(int i=1;i<=m;i++)
tr[rt][i].num=tr[rt<<1][i].num+tr[rt<<1|1][i].num;
}
void build(int l,int r,int rt){
if(l==r){
for(int i=1;i<=m;i++) tr[rt][i].num=tr[rt][i].tag=0;
tr[rt][1].num=1;
return ;
}
build(lson);build(rson);
pushup(rt);
}
void pushdown(int l,int r,int rt){
for(int i=1;i<=m;i++){
if(tr[rt][i].tag==1){
tr[rt][i].tag=0;
for(int j=1;j<=m;j++) tr[rt<<1][j].num=tr[rt<<1|1][j].num=tr[rt<<1|1][j].tag=tr[rt<<1][j].tag=0;
tr[rt<<1][i].tag=1;
tr[rt<<1|1][i].tag=1;
tr[rt<<1][i].num=mid-l+1;
tr[rt<<1|1][i].num=r-mid;
break;
}
}
}
void update(int l,int r,int rt,int a,int b,int x){
if(a<=l&&b>=r){
for(int i=1;i<=m;i++) tr[rt][i].num=tr[rt][i].tag=0;
tr[rt][x].num=r-l+1;
tr[rt][x].tag=1;
return ;
}
pushdown(l,r,rt);
if(a<=mid) update(lson,a,b,x);
if(b>mid) update(rson,a,b,x);
pushup(rt);
}
void query(int l,int r,int rt,int a,int b){
if(a<=l&&b>=r){
for(int i=1;i<=m;i++)
if(tr[rt][i].num) vis[i]=true;
return ;
}
pushdown(l,r,rt);
if(a<=mid) query(lson,a,b);
if(b>mid) query(rson,a,b);
return ;
}
int main(){
/*2023.6.24 hewanying P1558 色板游戏 线段树*/
scanf("%d%d%d",&n,&m,&q);
build(1,n,1);
for(int i=1;i<=q;i++){
char ch;int a,b,x;
scanf("\n%c",&ch);
if(ch=='C'){
scanf("%d%d%d",&a,&b,&x);
if(a>b) swap(a,b);
update(1,n,1,a,b,x);
}
else{
scanf("%d%d",&a,&b);
if(a>b) swap(a,b);
for(int j=1;j<=m;j++) vis[j]=false;
query(1,n,1,a,b);ans=0;
for(int j=1;j<=m;j++) if(vis[j]) ans++;
printf("%d\n",ans);
}
}
return 0;
}
P1502 窗口的星星
题目大意
给定一个平面上的
Solution
这道题和平面矩形有关
不难联想到扫描线
我们先把
将每个点按照
然后把扫描线沿
对于扫描线所在的位置
我们希望维护从
不妨用线段树来维护
我们希望查询到长度为
在线段树上,我们可以以
对于每一个点
我们把
每一次pushup都取max
最后直接输出
而考虑维护
我们可以利用差分的思想
对于每一个节点再建立一个节点
权值为
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int maxn=1e5+10;
int T,n,w,h,b[maxn];
struct node{
int x,y,hy,ly,lhy;
int val;
bool operator < (const node &rhs) const{
if(x!=rhs.x) return x<rhs.x;
return val>rhs.val;
}
}a[maxn];
int tr[maxn*4],ans=0,lazy[maxn*4];
void pushdown(int rt){
if(lazy[rt]!=0){
lazy[rt<<1]+=lazy[rt];
lazy[rt<<1|1]+=lazy[rt];
tr[rt<<1]+=lazy[rt];
tr[rt<<1|1]+=lazy[rt];
lazy[rt]=0;
}
}
void update(int l,int r,int rt,int a,int b,int x){
if(a<=l&&b>=r){
tr[rt]+=x;
lazy[rt]+=x;
return;
}
pushdown(rt);
if(a<=mid) update(lson,a,b,x);
if(b>mid) update(rson,a,b,x);
tr[rt]=max(tr[rt<<1],tr[rt<<1|1]);
}
signed main(){
/*2023.6.24 hewanying P1502 窗口的星星 线段树*/
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&n,&w,&h);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].val);
a[i].hy=a[i].y+h-1;
b[i]=a[i].y;b[n+i]=a[i].hy;
}
sort(b+1,b+2*n+1);sort(a+1,a+n+1);
int len=unique(b+1,b+2*n+1)-b-1;
for(int i=1;i<=len*4;i++) tr[i]=lazy[i]=0;
for(int i=1;i<=n;i++){
a[i].ly=lower_bound(b+1,b+len+1,a[i].y)-b;
a[i].lhy=lower_bound(b+1,b+len+1,a[i].hy)-b;
}
queue<int> q;ans=0;
for(int i=1;i<=n;i++){
while(!q.empty()&&a[q.front()].x+w<=a[i].x){
int t=q.front();
ans=max(ans,tr[1]);
update(1,len,1,a[t].ly,a[t].lhy,-a[t].val);//注意线段树的范围是1,len,1而不是1,n,1,这可能会造成递归越界而MLE
q.pop();
}
q.push(i);
update(1,len,1,a[i].ly,a[i].lhy,a[i].val);
ans=max(ans,tr[1]);
}
while(!q.empty()){
int t=q.front();
ans=max(ans,tr[1]);
update(1,len,1,a[t].ly,a[t].lhy,-a[t].val);
q.pop();
}
printf("%lld\n",ans);
}
return 0;
}
P1969 [NOIP2013 提高组] 积木大赛
题目大意
传送门
给定一个长度为
少
Solution
我的想法是先排序
从左到右枚举这个数消失后会不会对这个序列的段数进行影响
如果它两边都已经没有了
那么
如果它两边都还有数
那么
于是有了
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,ans=0,cnt=0;
bool vis[maxn];
struct node{
int id,val;
bool operator <(const node &rhs) const{
if(val!=rhs.val) return val<rhs.val;
return id<rhs.id;
}
}a[maxn];
int main(){
/*2023.7.2 H_W_Y P1969 [NOIP 2013提高组] 积木大赛 乱搞*/
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i].val),a[i].id=i;
cnt=1;
sort(a+1,a+n+1);
vis[0]=vis[n+1]=true;
for(int i=1;i<=n;i++){
ans+=cnt*(a[i].val-a[i-1].val);
if(vis[a[i].id-1]&&vis[a[i].id+1]) cnt--;
else if(!vis[a[i].id-1]&&!vis[a[i].id+1]) cnt++;
vis[a[i].id]=true;
}
printf("%d\n",ans);
return 0;
}
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
int n,x,ans=0,lst=0;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&x),ans+=(x>lst)?(x-lst):0,lst=x;
printf("%d\n",ans);
return 0;
}
P2471 [SCOI2007] 降雨量
题目大意
传送门
给定
以来降雨量最多的年份
Solution
静下心来分析和讨论
一共有9种情况
首先,不难想到是线段树
维护每一个区间的最大值
注意两个条件:
是 年以来降雨量最多的, 里都严格小于 年的降雨量不超过
于是乎:
mx表示已知的年份中
mx1表示已知的年份中
(每一条都在前面几条不成立的条件下)
年不确定
年不确定 年确定
年确定
年确定- 全部已知
- 还有未知
年不确定
几种情况想清楚即可
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m,x,y,l,r,b[maxn];
struct node{
int y,r,id;
}a[maxn];
struct Seg{
int val,id;
}tr[maxn*4],mx,mx1;
namespace SGT{
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
inline void pushup(int rt){
if(tr[lc].val>=tr[rc].val) tr[rt]=tr[lc];
else tr[rt]=tr[rc];
}
inline void update(int x,int val,int l=1,int r=n,int rt=1){
if(l==r){
tr[rt]=(Seg){val,x};
return;
}
if(x<=mid) update(x,val,lson);
else update(x,val,rson);
pushup(rt);
}
inline Seg Max(Seg p1,Seg p2){return p1.val>p2.val?p1:p2;}
inline Seg query(int x,int y,int l=1,int r=n,int rt=1){
if(x<=l&&y>=r) return tr[rt];
Seg res=(Seg){0,0};
if(x<=mid) res=Max(res,query(x,y,lson));
if(y>mid) res=Max(res,query(x,y,rson));
return res;
}
}
using namespace SGT;
int main(){
/*2023.7.3 H_W_Y P2471 [SCOI2007] 降雨量 Segment Tree*/
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].y,&a[i].r);
a[i].id=i,b[i]=a[i].y;
update(i,a[i].r);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&y,&x);
r=lower_bound(b+1,b+n+1,x)-b;
l=lower_bound(b+1,b+n+1,y)-b;
if(b[r]!=x){
if(b[l]!=y) printf("maybe\n");
else{
l++;r--;
if(l>r) printf("maybe\n");
else{
mx=query(l,r);
if(mx.val>=a[l-1].r) printf("false\n");
else printf("maybe\n");
}
}
}
else {
if(b[l]==y){
if(a[r].r>a[l].r) printf("false\n");
else{
l++;
mx=query(l,r);
if(l!=r) mx1=query(l,r-1);
else mx1.val=-0x3f3f3f3f;
if(mx.val!=a[r].r||mx.val==mx1.val) printf("false\n");
else{
if(r-l+1==x-y) printf("true\n");
else printf("maybe\n");
}
}
}
else{
mx=query(l,r);
if(l!=r) mx1=query(l,r-1);
else mx1.val=-0x3f3f3f3f;
if(mx.val!=a[r].r||mx.val==mx1.val) printf("false\n");
else printf("maybe\n");
}
}
}
return 0;
}
P1712 [NOI2016] 区间
题目描述
传送门
简单易懂
Solution
注意到这个问题是有关最大和最小的
那么不难想到可以利用到单调性
也就是说我们先将线段长度按照升序排序
然后用两个指针从头扫到尾
每一次就加入一条线段
在线段树上判断是否有一个点被覆盖的次数
如果有的话就从左开始缩小区间
以找到第一个最大覆盖次数
我们再继续加入下一条线段
线段树中维护的就是区间最大值
注意不要忘记判
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
const int N=1e6+5;
int ans=1e9,l,r,n,m,b[N],tr[N<<2],len,tag[N<<2];
struct node{
int l,r,len;
bool operator <(const node &rhs) const{
if(len!=rhs.len) return len<rhs.len;
return l<rhs.l;
}
}a[N];
void pushup(int rt){tr[rt]=max(tr[rt<<1],tr[rt<<1|1]);}
void pushdown(int rt){
if(tag[rt]){
tag[rt<<1]+=tag[rt];
tag[rt<<1|1]+=tag[rt];
tr[rt<<1]+=tag[rt];
tr[rt<<1|1]+=tag[rt];
tag[rt]=0;
}
}
void update(int l,int r,int rt,int a,int b,int x){
if(a<=l&&b>=r){
tr[rt]+=x;tag[rt]+=x;
return ;
}
pushdown(rt);
if(a<=mid) update(lson,a,b,x);
if(b>mid) update(rson,a,b,x);
pushup(rt);
}
int main(){
/*2023.7.27 H_W_Y P1712 [NOI2016] 区间 线段树*/
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].l,&a[i].r);
b[i]=a[i].l;b[i+n]=a[i].r;
a[i].len=a[i].r-a[i].l;
}
sort(b+1,b+n*2+1);
len=unique(b+1,b+n*2+1)-b-1;
for(int i=1;i<=n;i++) a[i].l=lower_bound(b+1,b+len+1,a[i].l)-b,a[i].r=lower_bound(b+1,b+len+1,a[i].r)-b;
sort(a+1,a+n+1);
l=1;r=0;
while(l<=n&&r<n){
r++;update(1,len,1,a[r].l,a[r].r,1);
while(tr[1]>=m){
ans=min(ans,a[r].len-a[l].len);
update(1,len,1,a[l].l,a[l].r,-1);
l++;
}
}
if(ans==1e9) printf("-1\n");
else printf("%d\n",ans);
return 0;
}
P2418 yyy loves OI IV
题目大意
传送门
给定
要么两种颜色差的绝对值不超过
Solution
不难想到可以用dp来维护
考虑先分别维护1和2的前缀和
令
这样
考虑到前缀和是单调的
那么前面两个状态我们可以直接维护(每次取min)
而对于第三个,我们考虑把式子化简:
我们令
那么我们要去找的是一个
且我们希望这里的
我们就把问题转化成了在以
找一个区间
就可以把问题转化到线段树上进行区间查询
每一次update在
最后维护区间最小值即可
注意:
对于前两个状态我们不管
否则若
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
const int maxn=5e5+10,inf=0x3f3f3f3f;
int n,m,sum[3][maxn],dp[maxn],ans[3][maxn],a[maxn],f[maxn];
struct seg{
int l,r,val;
}tr[maxn*8];
namespace SGT{
#define lc rt<<1
#define rc rt<<1|1
inline void pushup(int rt){tr[rt].val=min(tr[lc].val,tr[rc].val);}
inline void build(int l,int r,int rt){
tr[rt].l=l;tr[rt].r=r;tr[rt].val=inf;
if(l==r) return ;
int mid=l+((r-l)>>1);
build(l,mid,lc);
build(mid+1,r,rc);
}
inline void update(int x,int val,int rt=1){
if(tr[rt].l==tr[rt].r){
tr[rt].val=min(tr[rt].val,val);
return;
}
int mid=tr[rt].l+((tr[rt].r-tr[rt].l)>>1);
if(x<=mid) update(x,val,lc);
else update(x,val,rc);
pushup(rt);
}
inline int query(int a,int b,int rt=1){
if(a<=tr[rt].l&&b>=tr[rt].r) return tr[rt].val;
int res=inf;
int mid=tr[rt].l+((tr[rt].r-tr[rt].l)>>1);
if(a<=mid) res=min(res,query(a,b,lc));
if(b>mid) res=min(res,query(a,b,rc));
return res;
}
}
using namespace SGT;
inline int read(){
int x=0,ff=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') ff=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*ff;
}
int main(){
/*2023.7.5 H_W_Y P2418 yyy loves OI IV 线段树+DP*/
n=read();m=read();
for(int i=1;i<=n;i++){
a[i]=read();
sum[1][i]=sum[1][i-1]+(a[i]==1);
sum[2][i]=sum[2][i-1]+(a[i]==2);
f[i]=sum[1][i]-sum[2][i];
}
memset(dp,0x3f,sizeof(dp));
memset(ans,0x3f,sizeof(ans));
build(-500000,500000,1);
update(0,0);
dp[0]=0;
ans[1][0]=ans[2][0]=0;
for(int i=1;i<=n;i++){
dp[i]=min(min(ans[2][sum[2][i]]+1,dp[i-1]+1),min(ans[1][sum[1][i]]+1,query(f[i]-m,f[i]+m)+1));
ans[1][sum[1][i]]=min(ans[1][sum[1][i]],dp[i]);//两个都要更新
ans[2][sum[2][i]]=min(ans[2][sum[2][i]],dp[i]);
update(f[i],dp[i]);
}
printf("%d\n",dp[n]);
return 0;
}
P2572 [SCOI2010] 序列操作
题目大意
传送门
要求维护一个长度为
取反,区间
Solution
一道线段树的裸题(好久没遇到了)
但是为什么是紫题
很恶心的线段树,特别考验心态……
注意反转标记和覆盖标记的顺序
- 覆盖标记在反转标记之上
那么可以把反转标记清零 - 反转标记在覆盖标记之上
那么可以对覆盖标记直接修改
注意在pushdown的时候
对于每一个反转标记rev也要进行特殊处理
不要忘记了
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m,a[maxn],l,r,op;
struct seg{
int cnt[2],q[2],h[2],mx[2],tag,l,r,rev;
}tr[maxn*4];
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
namespace SGT{
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
inline void pushup(int rt){
tr[rt].cnt[0]=tr[lc].cnt[0]+tr[rc].cnt[0];
tr[rt].cnt[1]=tr[lc].cnt[1]+tr[rc].cnt[1];
tr[rt].q[0]=tr[lc].q[0];
tr[rt].q[1]=tr[lc].q[1];
if(tr[lc].q[0]==tr[lc].r-tr[lc].l+1) tr[rt].q[0]+=tr[rc].q[0];
if(tr[lc].q[1]==tr[lc].r-tr[lc].l+1) tr[rt].q[1]+=tr[rc].q[1];
tr[rt].h[0]=tr[rc].h[0];
tr[rt].h[1]=tr[rc].h[1];
if(tr[rc].h[0]==tr[rc].r-tr[rc].l+1) tr[rt].h[0]+=tr[lc].h[0];
if(tr[rc].h[1]==tr[rc].r-tr[rc].l+1) tr[rt].h[1]+=tr[lc].h[1];
tr[rt].mx[0]=max(tr[lc].mx[0],max(tr[rc].mx[0],tr[lc].h[0]+tr[rc].q[0]));
tr[rt].mx[1]=max(tr[lc].mx[1],max(tr[rc].mx[1],tr[lc].h[1]+tr[rc].q[1]));
}
inline void pushdown(int l,int r,int rt){
if(tr[rt].tag==1){
tr[lc].tag=tr[rc].tag=1;tr[lc].rev=tr[rc].rev=0;
tr[lc].cnt[0]=tr[rc].cnt[0]=0;
tr[lc].q[0]=tr[rc].q[0]=tr[lc].h[0]=tr[rc].h[0]=tr[lc].mx[0]=tr[rc].mx[0]=0;
tr[lc].cnt[1]=tr[lc].q[1]=tr[lc].h[1]=tr[lc].mx[1]=mid-l+1;
tr[rc].cnt[1]=tr[rc].q[1]=tr[rc].h[1]=tr[rc].mx[1]=r-mid;
tr[rt].tag=0;
}
if(tr[rt].tag==2){
tr[lc].tag=tr[rc].tag=2;tr[lc].rev=tr[rc].rev=0;
tr[lc].cnt[1]=tr[rc].cnt[1]=tr[lc].q[1]=tr[rc].q[1]=tr[lc].h[1]=tr[rc].h[1]=tr[lc].mx[1]=tr[rc].mx[1]=0;
tr[lc].cnt[0]=tr[lc].q[0]=tr[lc].h[0]=tr[lc].mx[0]=mid-l+1;
tr[rc].cnt[0]=tr[rc].q[0]=tr[rc].h[0]=tr[rc].mx[0]=r-mid;
tr[rt].tag=0;
}
if(tr[rt].rev==1){
swap(tr[lc].cnt[0],tr[lc].cnt[1]);
swap(tr[lc].q[0],tr[lc].q[1]);
swap(tr[lc].h[0],tr[lc].h[1]);
swap(tr[lc].mx[0],tr[lc].mx[1]);
if(tr[lc].tag==1) tr[lc].tag=2;
else if(tr[lc].tag==2) tr[lc].tag=1;//注意pushdown里tag下传要特殊处理
else tr[lc].rev^=1;
swap(tr[rc].cnt[0],tr[rc].cnt[1]);
swap(tr[rc].q[0],tr[rc].q[1]);
swap(tr[rc].h[0],tr[rc].h[1]);
swap(tr[rc].mx[0],tr[rc].mx[1]);
if(tr[rc].tag==1) tr[rc].tag=2;
else if(tr[rc].tag==2) tr[rc].tag=1;
else tr[rc].rev^=1;
tr[rt].rev=0;
}
}
inline void build(int l=1,int r=n,int rt=1){
tr[rt].l=l;tr[rt].r=r;
if(l==r){
tr[rt].cnt[a[l]]=tr[rt].q[a[l]]=tr[rt].h[a[l]]=tr[rt].mx[a[l]]=1;
tr[rt].tag=0;
return;
}
build(lson);build(rson);
pushup(rt);
}
inline void update(int x,int y,int val,int l=1,int r=n,int rt=1){//有可能是先覆盖,再反转
pushdown(l,r,rt);
if(x<=l&&y>=r){
if(val==1){
tr[rt].tag=val;tr[rt].rev=0;
tr[rt].cnt[1]=tr[rt].q[1]=tr[rt].h[1]=tr[rt].mx[1]=r-l+1;
tr[rt].cnt[0]=tr[rt].q[0]=tr[rt].h[0]=tr[rt].mx[0]=0;
}
if(val==2){
tr[rt].tag=val;tr[rt].rev=0;
tr[rt].cnt[1]=tr[rt].q[1]=tr[rt].h[1]=tr[rt].mx[1]=0;
tr[rt].cnt[0]=tr[rt].q[0]=tr[rt].h[0]=tr[rt].mx[0]=r-l+1;
}
if(val==3){
tr[rt].rev^=1;
swap(tr[rt].cnt[0],tr[rt].cnt[1]);
swap(tr[rt].q[0],tr[rt].q[1]);
swap(tr[rt].h[0],tr[rt].h[1]);
swap(tr[rt].mx[0],tr[rt].mx[1]);
}
return ;
}
pushdown(l,r,rt);
if(x<=mid) update(x,y,val,lson);
if(y>mid) update(x,y,val,rson);
pushup(rt);
}
inline int query_cnt(int x,int y,int l=1,int r=n,int rt=1){
if(x<=l&&y>=r) return tr[rt].cnt[1];
int res=0;
pushdown(l,r,rt);
if(x<=mid) res+=query_cnt(x,y,lson);
if(y>mid) res+=query_cnt(x,y,rson);
return res;
}
inline seg Max(seg x,seg y){
x.mx[1]=max(x.h[1]+y.q[1],max(x.mx[1],y.mx[1]));
if(y.h[1]==y.r-y.l+1) x.h[1]=x.h[1]+y.h[1];
else x.h[1]=y.h[1];
if(x.q[1]==x.r-x.l+1) x.q[1]+=y.q[1];
x.r=y.r;
return x;
}
inline seg query_mx(int x,int y,int l=1,int r=n,int rt=1){
if(x<=l&&y>=r) return tr[rt];
pushdown(l,r,rt);
if(x<=mid&&y>mid) return Max(query_mx(x,y,lson),query_mx(x,y,rson));
else if(x<=mid) return query_mx(x,y,lson);
else if(y>mid) return query_mx(x,y,rson);
}
}
using namespace SGT;
int main(){
/*2023.7.4 H_W_Y P2572 [SCOI2010] 序列操作 线段树*/
n=read();m=read();
for(int i=1;i<=n;i++) a[i]=read();
build();
for(int i=1;i<=m;i++){
op=read();l=read();r=read();
l++;r++;
if(op==0) update(l,r,2);
if(op==1) update(l,r,1);
if(op==2) update(l,r,3);
if(op==3) printf("%d\n",query_cnt(l,r));
if(op==4){
seg ans=query_mx(l,r);
printf("%d\n",ans.mx[1]);
}
}
return 0;
}
P2787 语文1(chin1)- 理理思维
题目大意
传送门
要求维护一个长度为
间
Solution
发现操作三其实是操作一和操作二结合起来实现
这样我们只需要维护操作一和操作二
暴力维护26棵线段树
也可以用珂朵莉树实现
线段树注意pushdown的写法
用到了mid就要传入参数l和r
H_W_Y-Coding
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,m,op,x,y,ch[maxn],k,lst;
char s[maxn];
struct seg{
int val,tag;
}tr[30][maxn*4];
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
namespace SGT{
int l,r;
#define mid (l+r)/2
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define lc rt<<1
#define rc rt<<1|1
inline void pushup(int p,int rt){tr[p][rt].val=tr[p][lc].val+tr[p][rc].val;}
inline void pushdown(int p,int l,int r,int rt){
if(tr[p][rt].tag!=0){
int t=tr[p][rt].tag;
tr[p][rt<<1].tag=tr[p][rt<<1|1].tag=t;
if(t==-1) tr[p][rt<<1].val=tr[p][rt<<1|1].val=0;
else tr[p][rt<<1].val=mid-l+1,tr[p][rt<<1|1].val=r-mid;
tr[p][rt].tag=0;
}
}
inline void build(int p,int l=1,int r=n,int rt=1){
if(l==r){
tr[p][rt].val=(ch[l]==p);
tr[p][rt].tag=0;
return;
}
tr[p][rt].tag=0;
build(p,lson);build(p,rson);
pushup(p,rt);
}
inline void update(int p,int a,int b,int x,int l=1,int r=n,int rt=1){
if(a<=l&&b>=r){
tr[p][rt].tag=x;
if(x==-1) tr[p][rt].val=0;
else tr[p][rt].val=(r-l+1);
return;
}
pushdown(p,l,r,rt);
if(a<=mid) update(p,a,b,x,lson);
if(b>mid) update(p,a,b,x,rson);
pushup(p,rt);
}
inline int query(int p,int a,int b,int l=1,int r=n,int rt=1){
if(a<=l&&b>=r) return tr[p][rt].val;
pushdown(p,l,r,rt);
int res=0;
if(a<=mid) res+=query(p,a,b,lson);
if(b>mid) res+=query(p,a,b,rson);
return res;
}
}
using namespace SGT;
inline int change(char p){
if(p>='A'&&p<='Z') return (p-'A')+1;
return (p-'a')+1;
}
int main(){
/*2023.7.4 H_W_Y P2787 语文1(chin1)- 理理思维 线段树*/
n=read();m=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++) ch[i]=change(s[i]);
for(int i=1;i<=26;i++) build(i);
for(int i=1;i<=m;i++){
op=read();x=read();y=read();
if(op==1){
scanf("%s",s);k=change(s[0]);
printf("%d\n",query(k,x,y));
}
if(op==2){
scanf("%s",s);k=change(s[0]);
for(int j=1;j<=26;j++) update(j,x,y,-1);
update(k,x,y,1);
}
if(op==3){
lst=x;
for(int j=1;j<=26;j++) ch[j]=query(j,x,y),update(j,x,y,-1);
for(int j=1;j<=26;j++)
if(ch[j]>0) update(j,lst,lst+ch[j]-1,1),lst=lst+ch[j];
}
}
return 0;
}
本文作者:H_W_Y
本文链接:https://www.cnblogs.com/H-W-Y/p/17495957.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步