区域增加 区域查询
利用两个树状数组 sum表示前缀和
void add(int i,int x,ll k)
{
for(;x<=n;x+=x&-x)
c[i][x]+=k;
}
ll ask(int i,int x)
{
ll ans=0;
for(;x;x-=x&-x)
ans+=c[i][x];
return ans;
}
add(0,l,k);
add(0,r+1,-k);
add(1,l,l*k);
add(1,r+1,-(r+1)*k);
ll cnt=sum[r]+(r+1)*ask(0,r)-ask(1,r);
cnt-=sum[l-1]+l*ask(0,l-1)-ask(1,l-1); ///区域和
if(l<=1) cnt+=a[0];
printf("%lld\n",cnt);
利用树状数组求出最长(非严格)上升子序列和最长(非严格)递减子序列
int lowbit(int x){return x&-x;}
int ask(int x)
{
int ans=0;
for(;x;x-=lowbit(x))
ans=max(ans,c[x]);
return ans;
}
int query(int x)
{
int ans=0;
for(;x<=maxx;x+=lowbit(x)) ///找前面比 x 大的最长序列
ans=max(ans,c[x]);
return ans;
}
void add(int x,int k) ///求最长单调递增序列
{
for(;x<=maxx;x+=lowbit(x))
c[x]=max(c[x],k);
}
void work(int x,int k) ///求非严格递减子序列
{
for(;x;x-=lowbit(x))
c[x]=max(c[x],k);
}
int main()
{
while(~scanf("%d",&x))
a[++n]=x,maxx=max(maxx,x);
int ans=0,num=0;
fr(i,1,n)
{
x=ask(a[i])+1; ///a[i]本身也是序列中一个
ans=max(ans,x);
add(a[i]+1,x); ///更新最大单调递增子序列
}
memset(c,0,sizeof(c));
fr(i,1,n)
{
x=query(a[i])+1;
num=max(num,x);
work(a[i],x); ///更新非严格单调递减序列
}
cout<<num<<endl<<ans<<endl;
return 0;
}
楼兰图腾
思路:求出左边比yi小的数 和右边比yi小的数 相乘就得出^的个数
同理 可以得出v的数
include[HTML_REMOVED]
using namespace std;
typedef long long ll ;
define debug() cout<<”fuck “;
define fr(k,n) for(int i=k;i<=n;i++)
define fo(k,n) for(int i=n;i>=k;i–)
const int mod = 107,N=2e5+5,INF=0x3f3f3f3f;
ll n,m,k,tot=0,a[N],b[N],sum[N];
ll l[2][N],r[2][N],cnt[2];
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<‘0’||ch>‘9’) {if(ch==’-‘) f=-1;ch=getchar();}
while(ch>=‘0’&& ch<=‘9’) {x=x10+ch-‘0’;ch=getchar();}
return fx;
}
void add(int x,int k)
{
for(;x<=tot;x+=x&-x)
sum[x]+=k;
}
int ask(int x)
{
int ans=0;
for(;x;x-=x&-x)
ans+=sum[x];
return ans;
}
int main()
{
n=read();
fr(1,n) a[i]=read(),b[i]=a[i];
sort(b+1,b+n+1);
tot=unique(b+1,b+n+1)-b-1;
fr(1,n)
{
int x=lower_bound(b+1,b+tot+1,a[i])-b;
l[0][i]=ask(x-1); ///左边比yi小的数
l[1][i]=ask(tot)-ask(x);
add(x,1);
}
memset(sum,0,sizeof(sum));
fo(1,tot)
{
int x=lower_bound(b+1,b+tot+1,a[i])-b;
r[0][i]=ask(x-1);
/ cout<<ask(x-1)<<” “;/
r[1][i]=ask(tot)-ask(x);
add(x,1);
}
fr(1,n)
cnt[0]+=l[0][i]r[0][i],cnt[1]+=l[1][i]r[1][i];
cout<<cnt[1]<<” “<<cnt[0];
return 0;
}
P3655 不成熟的梦想家
思路:每次增加一个区域的值 区域间的相对值不会改变 改变的只是 l-1和 l 以及 r 和 r-1 之间的数值 因此利用树状数组记录差分 每次修改时进行单点查询求出答案
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=5e5+5,INF=0x3f3f3f3f;
ll n,m,k,tot=0,a[N],c[N],s,t;
ll sum=0;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void add(int x,int k)
{
for(;x<=n;x+=x&-x)
c[x]+=k;
}
ll ask(int x)
{
ll ans=0;
for(;x;x-=x&-x)
ans+=c[x];
return ans;
}
int main()
{
n=read(),m=read(),s=read(),t=read();
fr(i,0,n) a[i]=read();
fr(i,1,n)
{
if(a[i]>a[i-1]) sum-=s*abs(a[i]-a[i-1]);
else sum+=t*abs(a[i]-a[i-1]);
}
fr(i,1,m)
{
ll l=read(),r=read(),k=read();
ll _l=a[l-1]+ask(l-1),_r=a[r+1]+ask(r+1);
ll l_=a[l]+ask(l),r_=a[r]+ask(r);
if(_l<l_) sum+=s*abs(_l-l_);
else sum-=t*abs(_l-l_);
if(r_<_r) sum+=s*abs(_r-r_);
else sum-=t*abs(_r-r_);
add(l,k),add(r+1,-k);
l_=a[l]+ask(l),r_=a[r]+ask(r);
_l=a[l-1]+ask(l-1),_r=a[r+1]+ask(r+1);
if(_l<l_) sum-=s*abs(_l-l_);
else sum+=t*abs(_l-l_);
if(r_<_r) sum-=s*abs(_r-r_);
else sum+=t*abs(_r-r_);
printf("%lld\n",sum);
}
return 0;
}
AcWing 246. 区间最大公约数
思路:题目类型使区域增加和区域求gcd 关于区域上的问题可以用线段树处理。
gcd(a,b)==gcd(a-nb,b) 通过扩展到多个数的gcd得到gcd(a,b,c)=gcd(a,b-a,c-b) 利用差分记录每个点的增加情况.如果区间[l,r]增加了c则gcd(l+c,l+1+c,…,r+c)=gcd(l+c,l+1,…,r)=gcd((l+c),(l+1,l+2,…,r))
l+c 是l加上差分的值 在线段树上差分的值最终通过树状数组可以重新加回来 因此不会改变
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=5e5+5,INF=0x3f3f3f3f;
ll n,m,a[N],c[N],d[N];
char ch;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
long long gcd(ll a,ll b)
{
return b?gcd(b,a%b):a;
}
struct node
{
ll l,r;
ll dat,add;
}b[N*4];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
if(l==r)
{
b[p].dat=c[l];
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
b[p].dat=gcd(b[p*2].dat,b[p*2+1].dat);
}
void change(int p,int l,ll r)
{
if(b[p].l==b[p].r)
{
b[p].dat+=r;
return ;
}
int bl=b[p].l,br=b[p].r;
int mid=(bl+br)>>1;
if(l<=mid) change(p*2,l,r);
else change(p*2+1,l,r);
b[p].dat=gcd(b[p*2].dat,b[p*2+1].dat);
}
ll ask(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].dat;
int mid=(b[p].l+b[p].r)>>1;
ll ans=0;
if(l<=mid) ans=gcd(ask(p*2,l,r),ans);
if(mid<r) ans=gcd(ask(p*2+1,l,r),ans);
return abs(ans);
}
void add(int x,ll k)
{
for(;x<=n;x+=x&-x)
d[x]+=k;
}
ll sum(int x)
{
ll ans=0;
for(;x;x-=x&-x)
ans+=d[x];
return ans;
}
int main()
{
n=read(),m=read();
fr(i,1,n) a[i]=read(),c[i]=a[i]-a[i-1];
build(1,1,n);
fr(i,1,m)
{
cin>>ch;
int l=read(),r=read();
if(ch=='Q')
{
ll ans=a[l]+sum(l); ///树状数组记录差值
ans=gcd(ans,ask(1,l+1,r));
printf("%lld\n",ans);
}
else
{
ll k=read();
change(1,l,k);
if(r<n) change(1,r+1,-k);
add(l,k);
add(r+1,-k);
}
}
return 0;
}
P3373 【模板】线段树 2
思路:比一般的线段树多增加了一个乘法运算 所以得规定在传递到子节点时的运算优先级
如果 现在[1,3]区间内增加 3,然后在[1,3]区间乘以2.结果应该时 sum[1,3]*2+3 *2 所以在每次乘以一个数k时,应将加数位add2也乘以k,从而避免了在传递到子节点时出现错误
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=1e5+5,INF=0x3f3f3f3f;
int n,m,a[N],t;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
ll dat,add1,add2;
}b[N*4];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
b[p].add1=1,b[p].add2=0;
if(l==r)
{
b[p].dat=a[l];
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
b[p].dat=(b[p*2].dat+b[p*2+1].dat)%t;
}
void spread(int p)
{
b[p*2].dat=(b[p].add1*b[p*2].dat+b[p].add2*(b[p*2].r-b[p*2].l+1))%t;
b[p*2+1].dat=(b[p].add1*b[p*2+1].dat+b[p].add2*(b[p*2+1].r-b[p*2+1].l+1))%t;
b[p*2].add1=(b[p].add1*b[p*2].add1)%t;
b[p*2+1].add1=(b[p].add1*b[p*2+1].add1)%t;
b[p*2].add2=(b[p].add1*b[p*2].add2+b[p].add2)%t; ///乘以一个数k时 已经存在但没有更新的加数会增大k倍
b[p*2+1].add2=(b[p].add1*b[p*2+1].add2+b[p].add2)%t;
b[p].add1=1;
b[p].add2=0;
}
void change1(int p,int l,int r,int k)
{
int bl=b[p].l,br=b[p].r;
if(l<=b[p].l&&b[p].r<=r)
{
b[p].dat=(k*b[p].dat)%t;
b[p].add1=(k*b[p].add1)%t; ///同理
b[p].add2=(b[p].add2*k)%t;
return ;
}
spread(p);
int mid=(bl+br)>>1;
if(l<=mid) change1(p*2,l,r,k);
if(r>mid) change1(p*2+1,l,r,k);
b[p].dat=(b[p*2].dat+b[p*2+1].dat)%t;
}
void change2(int p,int l,int r,int k)
{
int bl=b[p].l,br=b[p].r;
if(l<=b[p].l&&b[p].r<=r)
{
b[p].dat=(b[p].dat+k*(br-bl+1))%t;
b[p].add2=(b[p].add2+k)%t;
return ;
}
spread(p);
int mid=(bl+br)>>1;
if(l<=mid) change2(p*2,l,r,k);
if(r>mid) change2(p*2+1,l,r,k);
b[p].dat=(b[p*2].dat+b[p*2+1].dat)%t;
}
ll ask(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].dat%t;
spread(p);
int mid=(b[p].l+b[p].r)>>1;
ll ans=0;
if(l<=mid) ans+=ask(p*2,l,r);
if(mid<r) ans+=ask(p*2+1,l,r);
/*cout<<ans<<" ";*/
return ans%t;
}
int main()
{
n=read(),m=read(),t=read();
fr(i,1,n) a[i]=read();
build(1,1,n);
fr(i,1,m)
{
int ch=read();
int l=read(),r=read();
if(ch==3)
printf("%lld\n",ask(1,l,r));
else
{
ll k=read();
if(ch==1) change1(1,l,r,k);
else change2(1,l,r,k);
}
}
return 0;
}
P1438 无聊的数列
思路:类似于树状数组的区域增加和单点求和 但是本题是增加一个等差数列 所以用线段树去记录一个差分
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=2e5+5,INF=0x3f3f3f3f;
int n,m,a[N];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
int dat,add;
}b[N*4];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
b[p].add=0;
if(l==r)
return ;
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void spread(int p)
{
if(b[p].add)
{
int l=b[p].l,r=b[p].r;
int mid=(l+r)>>1;
b[p*2].dat+=(mid-l+1)*b[p].add;
b[p*2+1].dat+=(r-mid)*b[p].add;
b[p*2].add+=b[p].add;
b[p*2+1].add+=b[p].add;
b[p].add=0;
}
}
void change(int p,int l,int r,int k)
{
int bl=b[p].l,br=b[p].r;
if(l<=b[p].l&&b[p].r<=r)
{
b[p].dat+=(b[p].r-b[p].l+1)*k;
b[p].add+=k;
return ;
}
spread(p);
int mid=(bl+br)>>1;
if(l<=mid) change(p*2,l,r,k);
if(r>mid) change(p*2+1,l,r,k);
b[p].dat=(b[p*2].dat+b[p*2+1].dat);
}
int ask(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].dat;
spread(p);
int mid=(b[p].l+b[p].r)>>1;
int ans=0;
if(l<=mid) ans+=ask(p*2,l,r);
if(r>mid) ans+=ask(p*2+1,l,r);
return ans;
}
int main()
{
n=read(),m=read();
fr(i,1,n) a[i]=read();
build(1,1,n);
fr(i,1,m)
{
int ch=read();
int l=read(),r,k,d;
if(ch==2)
{
printf("%d\n",a[l]+ask(1,1,l));
}
else
{
scanf("%d%d%d",&r,&k,&d);
change(1,l,l,k);
if(r>l)
change(1,l+1,r,d);
if(r!=n)
change(1,r+1,r+1,-(k+(r-l)*d)); ///改为 -k-(r-l)*d 就RE了 真离谱
}
}
return 0;
}
P3740 [HAOI2014]贴海报 染色模板题
思想:N太大了因此想到离散化数据 但是如果区间 [2,4] 和区间 [6,8]经过离散后是 [2,3]和[4,6] 查询时会有冲突 因此每次在数据中加上一个r+1 从而防止离散后导致的查询错误
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=1e7+5,INF=0x3f3f3f3f;
int n,m,a[N*2],tot=0;
ll ans=0;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
ll dat,add;
}b[N*2];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
if(l==r)
return ;
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
if(b[p*2].dat==b[p*2].dat)
b[p].dat=b[p*2].dat;
else b[p].dat=-1; ///-1表示该区间有不同的颜色
}
void spread(int p)
{
if(b[p].add) ///修改子区间的颜色
{
b[p*2].dat=b[p].add;
b[p*2+1].dat=b[p].add;
b[p*2].add=b[p].add;
b[p*2+1].add=b[p].add;
b[p].add=0;
}
}
void change(int p,int l,int r,ll k)
{
int bl=b[p].l,br=b[p].r;
if(l<=b[p].l&&b[p].r<=r)
{
b[p].dat=k;
b[p].add=k;
return ;
}
spread(p);
int mid=(bl+br)>>1;
if(l<=mid) change(p*2,l,r,k);
if(r>mid) change(p*2+1,l,r,k);
if(b[p*2+1].dat==b[p*2].dat)
b[p].dat=b[p*2].dat;
else b[p].dat=-1;
}
int color[1005];
void ask(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
{
if(b[p].dat==-1) ///区间有不同的颜色向下寻找
{
ask(p*2,l,r);
ask(p*2+1,l,r);
}
else if(b[p].dat)
{
if(!color[b[p].dat])
ans++,color[b[p].dat]=1;
}
return ;
}
spread(p);
int mid=(b[p].l+b[p].r)>>1;
ask(p*2,l,r);
ask(p*2+1,l,r);
}
int main()
{
n=read(),m=read();
int l[1005],r[1005];
fr(i,1,m)
{
l[i]=read(),r[i]=read();
a[++tot]=l[i],a[++tot]=r[i],a[++tot]=r[i]+1; ///防止离散导致覆盖问题
}
sort(a+1,a+tot+1);
int num=unique(a+1,a+tot+1)-a-1; ///数据过大 因此需要离散化区间
build(1,1,num);
fr(i,1,m)
{
int nl=lower_bound(a+1,a+num+1,l[i])-a;
int nr=lower_bound(a+1,a+num+1,r[i])-a;
change(1,nl,nr,i);
}
ask(1,1,num);
cout<<ans<<endl;
return 0;
}
P5490 【模板】扫描线 线段树模板题
思想:扫描线 这篇博客很讲的很清楚就不再赘诉,但是有一点就是线段树维护的是扫描线之间的矩形宽度。线段树中的cnt=-1表示出边 cnt=1表示入边,每次扫描到一条边时修改线段树维护的距离:
- 如果线段树中区域cnt值大于0说明区域被包含在内,那么该区域维护的值就是其本身的区域
- 如果线段树中的cnt=0 表示该区域可能部分包含在内 因此该区域维护的值是其两个子节点维护值的和
- 若该区域是叶子节点 len=0
线段树中节点表示的是区域而不是点,因此[l.l]区域表示的是点[l,l+1] ,所以每次维护的时候要进行+1或者-1操作。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=1e6+5,INF=0x3f3f3f3f;
ll n,m,tot=0,tx[N*2];
ll ans=0;
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
ll l,r,h;
int cnt;
bool operator <(node x)
{
return h<x.h;
}
}c[N*2];
struct MVP ///线段树维护的是扫描线之间 r-1的距离
{
int l,r,cnt;
ll len;
}b[N*4];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
b[p].cnt=0,b[p].len=0;
if(l==r)
return ;
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void pushup(int p)
{
if(b[p].cnt)
b[p].len=tx[b[p].r+1]-tx[b[p].l]; ///线段树的节点表示的是区间 因此[l,r] 表示的是 [l,r+1]
else if(b[p].l==b[p].r)
b[p].len=0;
else
b[p].len=b[p*2].len+b[p*2+1].len;
}
void change(int p,int l,int r,int k)
{
int bl=b[p].l,br=b[p].r;
if(l<=b[p].l&&b[p].r<=r)
{
b[p].cnt+=k;
pushup(p); /// 及时更改距离
return ;
}
int mid=(bl+br)>>1;
if(l<=mid) change(p*2,l,r,k);
if(r>mid) change(p*2+1,l,r,k);
pushup(p);
}
int main()
{
n=read();
fr(i,1,n)
{
ll x=read(),y=read(),xx=read(),yy=read();
c[++tot]=node{x,xx,y,1};
tx[tot]=x;
c[++tot]=node{x,xx,yy,-1};
tx[tot]=xx;
}
sort(tx+1,tx+tot+1);
sort(c+1,c+tot+1);
build(1,1,tot);
fr(i,1,tot)
{
int l=lower_bound(tx+1,tx+tot+1,c[i].l)-tx; ///数据过大离散化
int r=lower_bound(tx+1,tx+tot+1,c[i].r)-tx-1;
change(1,l,r,c[i].cnt);
ans+=b[1].len*(c[i+1].h-c[i].h); ///b[1].len 表示两条扫描线之间 r-1距离
}
printf("%lld",ans);
return 0;
}
P6492 [COCI2010-2011#6] STEP
思路:果然问题还是自己想复杂了,得熟练才行 本题解法类似区间最大和的解法:如果a[mid]!=a[mid+1],则合并左子节点的右区间最大值和右节点的左区间最大值。注意更新节点左右子区间最大值时,如果b[p2].lmax==b[p2].len 则b[p].lmax=b[p2].len+b[p2+1].lmax 否则 b[p].lmax=b[p*2+1].lmax。右节点同理
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=2e5+5,INF=0x3f3f3f3f;
int n,m,a[N],t;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
int lmax,rmax,len,maxlen;
}b[N*4];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
b[p].len=r-l+1;
b[p].lmax=b[p].rmax=b[p].maxlen=1;
if(l==r)
{
a[l]=0;
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void change(int p,int x)
{
int bl=b[p].l,br=b[p].r;
if(b[p].l==b[p].r)
{
a[bl]^=1;
return ;
}
int mid=(bl+br)>>1;
if(x<=mid) change(p*2,x);
else change(p*2+1,x);
if(a[mid]!=a[mid+1])
{
b[p].maxlen=b[p*2].rmax+b[p*2+1].lmax;
b[p].maxlen=max(b[p].maxlen,b[p*2].maxlen);
b[p].maxlen=max(b[p].maxlen,b[p*2+1].maxlen);
if(b[p*2].len==b[p*2].lmax) b[p].lmax=b[p*2].len+b[p*2+1].lmax;
else b[p].lmax=b[p*2].lmax;
if(b[p*2+1].len==b[p*2+1].rmax) b[p].rmax=b[p*2+1].len+b[p*2].rmax;
else b[p].rmax=b[p*2+1].rmax;
}
else
{
b[p].lmax=b[p*2].lmax,b[p].rmax=b[p*2+1].rmax;
b[p].maxlen=max(b[p*2].maxlen,b[p*2+1].maxlen);
}
/* cout<<"("<<b[p].l<<","<<b[p].r<<") "<<b[p].lmax<<" "<<b[p].rmax<<" "<<b[p].maxlen<<endl;*/
}
int main()
{
n=read(),m=read();
build(1,1,n);
fr(i,1,m)
{
int x=read();
change(1,x);
printf("%d\n",b[1].maxlen);
}
return 0;
}
/*
ll ask(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].dat;
spread(p);
int mid=(b[p].l+b[p].r)>>1;
ll ans=0;
if(l<=mid) ans+=ask(p*2,l,r);
if(mid<r) ans+=ask(p*2+1,l,r);
return ans%t;
}
void spread(int p)
{
b[p*2].dat=(b[p*2].dat*b[p].add1+b[p].add2*(b[p*2].r-b[p*2].l+1))%t;
b[p*2+1].dat=(b[p*2+1].dat*b[p].add1+b[p].add2*(b[p*2+1].r-b[p*2+1].l+1))%t;
b[p*2].add1=(b[p].add1*b[p*2].add1)%t;
b[p*2+1].add1=(b[p*2+1].add1*b[p].add1)%t;
b[p*2].add2=(b[p*2].add2*b[p].add1+b[p].add2)%t;
b[p*2+1].add2=(b[p*2+1].add2*b[p].add1+b[p].add2)%t;
b[p].add2=0; b[p].add1=1;
}
*/
Count the Colors
思路:一道染色模板修改的题目,利用color数组记录颜色。需要注意的是如果左子区间的最右一段的颜色和右子区间的最左一段颜色相同则-1。还有线段树维护的是区间,区间是一个左闭右开的区间,因此每次修改时 r要减一。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 107,N=1e4+5,INF=0x3f3f3f3f;
int n,m,Max=-1;
int l[N],r[N],t[N];
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
int dat,add;
int lc,rc;
}b[N*4];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
b[p].dat=b[p].add=0;
b[p].lc=b[p].rc=0;
if(l==r)
return ;
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
}
void spread(int p)
{
if(b[p].add)
{
b[p*2].dat=b[p].add;
b[p*2+1].dat=b[p].add;
b[p*2].add=b[p*2+1].add=b[p].add;
b[p*2].lc=b[p*2].rc=b[p].add;
b[p*2+1].lc=b[p*2+1].rc=b[p].add;
b[p].add=0;
}
}
void change(int p,int l,int r,int k)
{
if(l<=b[p].l&&b[p].r<=r)
{
b[p].dat=k;
b[p].lc=b[p].rc=k;
b[p].add=k;
return ;
}
spread(p);
int mid=(b[p].l+b[p].r)>>1;
if(l<=mid) change(p*2,l,r,k);
if(r>mid) change(p*2+1,l,r,k);
///***
b[p].lc=b[p*2].lc,b[p].rc=b[p*2+1].rc;
if(b[p*2].dat==b[p*2+1].dat)
b[p].dat=b[p*2].dat;
else b[p].dat=-1;
}
int color[N];
void ask(int p)
{
spread(p);
/*cout<<"("<<b[p].l<<","<<b[p].r<<") "<<b[p].lc-1<<" "<<b[p].rc-1<<" "<<b[p].dat-1<<endl;*/
if(b[p].dat==-1)
{
ask(p*2);
ask(p*2+1);
int mid=(b[p].l+b[p].r)>>1;
if(b[p*2].rc==b[p*2+1].lc) color[b[p*2].rc]--;
}
else if(b[p].dat)
color[b[p].dat]++;
}
int main()
{
while(~scanf("%d",&n))
{
memset(b,0,sizeof(b));
memset(color,0,sizeof(color));
fr(i,1,n)
l[i]=read(),r[i]=read(),t[i]=read()+1,Max=max(Max,r[i]);
build(1,0,Max);
fr(i,1,n)
change(1,l[i],r[i]-1,t[i]); ///r[i]-1 线段树中的节点不是节点而是区域 例如[0,2]区间大小是3 但是只是把前两块涂色了 与2这个点无关
ask(1);
fr(i,1,8005)
if(color[i]) printf("%d %d\n",i-1,color[i]);
printf("\n");
}
return 0;
}
P1471 方差
思路:区域修改和区域查询 线段树解决此类问题。
此题困难点推公式:方差 s^2 = ∑(xi-x) ^2/n
由于方差中需要将每个因子都要计算,对于线段树而言时间复杂度过大,因此需要拆分得到公式
∑xi^ 2/n - 2x∑xi +x^ 2= ∑xi^ 2/n -x^2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck "<<endl;
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 1000000007,N=1e5+5,INF=0x3f3f3f3f;
int n,m;
double c[N],a[N];
inline ll read()
{
ll x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
double dat,add,num;
}b[N*4];
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
b[p].add=0;
if(b[p].l==b[p].r)
{
b[p].dat=a[l]*a[l];
b[p].num=a[l];
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
b[p].dat=b[p*2].dat+b[p*2+1].dat;
b[p].num=b[p*2].num+b[p*2+1].num;
}
void spread(int p)
{
if(b[p].add)
{
double k=b[p].add;
b[p*2].dat+=2*k*b[p*2].num+(b[p*2].r-b[p*2].l+1)*k*k; ///先求平方数 在将add加上
b[p*2+1].dat+=2*k*b[p*2+1].num+(b[p*2+1].r-b[p*2+1].l+1)*k*k;
b[p*2].num+=b[p].add*(b[p*2].r-b[p*2].l+1);
b[p*2+1].num+=b[p].add*(b[p*2+1].r-b[p*2+1].l+1);
b[p*2].add+=k,b[p*2+1].add+=k;
b[p].add=0;
}
}
void change(int p,int l,int r,double k)
{
if(l<=b[p].l&&b[p].r<=r)
{
b[p].dat+=2*k*b[p].num+(b[p].r-b[p].l+1)*k*k;
b[p].num+=k*(b[p].r-b[p].l+1);
b[p].add+=k;
return ;
}
spread(p);
int mid=(b[p].l+b[p].r)>>1;
if(l<=mid) change(p*2,l,r,k);
if(r>mid) change(p*2+1,l,r,k);
b[p].dat=b[p*2].dat+b[p*2+1].dat;
b[p].num=b[p*2].num+b[p*2+1].num;
}
double ask(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].dat;
spread(p);
int mid=(b[p].l+b[p].r)>>1;
double ans=0;
if(l<=mid) ans+=ask(p*2,l,r);
if(r>mid) ans+=ask(p*2+1,l,r);
return ans;
}
double askave(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].num;
spread(p);
int mid=(b[p].l+b[p].r)>>1;
double ans=0;
if(l<=mid) ans+=askave(p*2,l,r);
if(r>mid) ans+=askave(p*2+1,l,r);
return ans;
}
int main()
{
n=read(),m=read();
fr(i,1,n) scanf("%lf",&a[i]);
build(1,1,n);
fr(i,1,m)
{
int ch,l,r;
scanf("%d%d%d",&ch,&l,&r);
if(ch==1)
{
double k;
scanf("%lf",&k);
change(1,l,r,k);
}
if(ch==2)
{
double sum=askave(1,l,r);
printf("%.4lf\n",sum/(double)(r-l+1));
}
if(ch==3)
{
double ave=askave(1,l,r)/(r-l+1);
double sum=ask(1,l,r)/(r-l+1)-ave*ave;
printf("%.4lf\n",sum);
}
}
return 0;
}
P2894 [USACO08FEB]Hotel G
思路:操作1是找到连续的x间房并入住,操作2是将l到r的房间清空。区域维护利用线段树。
思路和之前做的题一样就是维护一个区间最大空房间的数量:利用lmax和rmax 以及sum去维护一个区域最大和。
P6492 [COCI2010-2011#6] STEP
这个题的思路和本题很相似,只不过查询一个最大值
void pushup(int p)
{
if(b[p*2].lmax==b[p*2].len) ///更新区间最大左边和右边连续房间数量,区间最大房间数
b[p].lmax=b[p*2].len+b[p*2+1].lmax;
else b[p].lmax=b[p*2].lmax;
if(b[p*2+1].rmax==b[p*2+1].len)
b[p].rmax=b[p*2+1].len+b[p*2].rmax;
else b[p].rmax=b[p*2+1].rmax;
b[p].dat=max((b[p*2].rmax+b[p*2+1].lmax),max(b[p*2].dat,b[p*2+1].dat)); ///区间最大房间数量
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
#define debug() cout<<"fuck ";
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 1000000007,N=1e5+5,INF=0x3f3f3f3f;
int n,m,tot=0,t,a[N];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r;
int lmax,rmax,dat,add,len;
}b[N*4];
void pushup(int p)
{
if(b[p*2].lmax==b[p*2].len) ///更新区间最大左边和右边连续房间数量,区间最大房间数
b[p].lmax=b[p*2].len+b[p*2+1].lmax;
else b[p].lmax=b[p*2].lmax;
if(b[p*2+1].rmax==b[p*2+1].len)
b[p].rmax=b[p*2+1].len+b[p*2].rmax;
else b[p].rmax=b[p*2+1].rmax;
b[p].dat=max((b[p*2].rmax+b[p*2+1].lmax),max(b[p*2].dat,b[p*2+1].dat)); ///区间最大房间数量
}
void spread(int p)
{
if(b[p].add==1)
{
b[p*2].dat=b[p*2].lmax=b[p*2].rmax=0;
b[p*2+1].dat=b[p*2+1].lmax=b[p*2+1].rmax=0;
b[p*2].add=b[p].add,b[p*2+1].add=b[p].add;
b[p].add=0;
}
else if(b[p].add==2)
{
b[p*2].dat=b[p*2].lmax=b[p*2].rmax=b[p*2].len;
b[p*2+1].dat=b[p*2+1].lmax=b[p*2+1].rmax=b[p*2+1].len;
b[p*2].add=b[p].add,b[p*2+1].add=b[p].add;
b[p].add=0;
}
}
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
b[p].len=r-l+1;
b[p].add=0;
if(b[p].l==b[p].r)
{
b[p].dat=b[p].r-b[p].l+1;
b[p].lmax=b[p].rmax=b[p].dat;
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
pushup(p);
}
void change(int p,int l,int r,int k)
{
if(l<=b[p].l&&b[p].r<=r)
{
if(k==1) ///住房
{
b[p].dat=0;
b[p].lmax=b[p].rmax=0;
}
else ///退房 房间空出来
{
b[p].dat=b[p].len;
b[p].lmax=b[p].rmax=b[p].len;
}
b[p].add=k;
return ;
}
spread(p);
int mid=(b[p].l+b[p].r)>>1;
if(l<=mid) change(p*2,l,r,k);
if(r>mid) change(p*2+1,l,r,k);
pushup(p);
}
int ask(int p,int len)
{
spread(p);
int l=b[p].l,r=b[p].r;
if(l==r) return l;
int mid=(l+r)>>1;
if(b[p*2].dat>=len) return ask(p*2,len);
if(b[p*2].rmax+b[p*2+1].lmax>=len) return mid-b[p*2].rmax+1;
return ask(p*2+1,len);
}
int main()
{
n=read(),m=read();
build(1,1,n);
fr(i,1,m)
{
int ch,len;
scanf("%d",&ch);
if(ch==1)
{
int len=read();
if(b[1].dat<len)
{
printf("0\n");
continue;
}
int ans=ask(1,len);
printf("%d\n",ans);
change(1,ans,ans+len-1,1);
}
else
{
int l=read(),r=read();
change(1,l,l+r-1,2);
/*debug();cout<<b[1].dat<<" "<<b[1].lmax<<" "<<b[1].rmax<<endl;*/
}
}
return 0;
}
P1972 HH的项链
题解:r从小到大排序,树状数组记录。如果当前种类贝壳出现过,从树状数组中删去之前位置的权值,在r位置上加上1。因为r增加单调,保证了贝壳颜色的有序性。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 998244353,N=1e6+5,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m,ans[N];
int a[N],pos[N],c[N];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
struct node
{
int l,r,id;
}b[N];
bool cmp1(node x,node y)
{
return x.r<y.r;
}
bool cmp2(node x,node y)
{
return x.id<y.id;
}
int lowbit(int x){return x&-x;}
void add(int x,int k)
{
for(;x<=n;x+=lowbit(x))
c[x]+=k;
}
int ask(int x)
{
int ans=0;
for(;x;x-=lowbit(x))
ans+=c[x];
return ans;
}
int main()
{
/*std::ios::sync_with_stdio(false);
cin.tie(0);*/
n=read();
fr(i,1,n) a[i]=read();
m=read();
fr(i,1,m) b[i].l=read(),b[i].r=read(),b[i].id=i;
sort(b+1,b+m+1,cmp1);
int r=0;
fr(i,1,m)
{
while(r<b[i].r)
{
++r;
if(pos[a[r]]) add(pos[a[r]],-1); ///只需要对r进行转移,删除之前存在的颜色,在r处增加
pos[a[r]]=r;
add(r,1);
}
int id=b[i].id;
ans[id]=ask(b[i].r)-ask(b[i].l-1);
}
fr(i,1,m) printf("%d\n",ans[i]);
return 0;
}
P6327 区间加区间sin和
思想: sin(a+b)=sinacosb+cosasinb
cos(a+b)=cosacosb-sinasinb
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
using namespace std;
const int N=2e5+5,INF=0x3f3f3f3f;
int n,m; ll last=0;
int a[N];
struct node
{
int l,r;
ll val,add;
double sums,sumc,sum;
}b[N*4];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void pushup(int p)
{
b[p].sums=b[p*2].sums+b[p*2+1].sums;
b[p].sumc=b[p*2].sumc+b[p*2+1].sumc;
}
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r,b[p].add=0;
if(l==r)
{
b[p].sums=sin(a[l]);
b[p].sumc=cos(a[l]);
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
pushup(p);
}
void spread(int p)
{
if(b[p].add)
{
double Sin=sin(b[p].add),Cos=cos(b[p].add);
double now1=b[p*2].sums,now2=b[p*2+1].sums;
b[p*2].sums=b[p*2].sums*Cos+b[p*2].sumc*Sin; ///sin(a+b)=sinacosb+sinbcosa
b[p*2+1].sums=b[p*2+1].sums*Cos+b[p*2+1].sumc*Sin;
b[p*2].sumc=b[p*2].sumc*Cos-now1*Sin; ///cos(a+b)=cosacosb-sinasinb
b[p*2+1].sumc=b[p*2+1].sumc*Cos-now2*Sin;
b[p*2].add+=b[p].add;
b[p*2+1].add+=b[p].add;
b[p].add=0;
}
}
void change(int p,int l,int r,int v)
{
if(l<=b[p].l&&b[p].r<=r)
{
b[p].add+=v;
double sums=b[p].sums;
b[p].sums=b[p].sums*cos(v)+sin(v)*b[p].sumc;
b[p].sumc=b[p].sumc*cos(v)-sums*sin(v);
return ;
}
spread(p);
int mid=(b[p].l+b[p].r)>>1;
if(l<=mid) change(p*2,l,r,v);
if(r>mid) change(p*2+1,l,r,v);
pushup(p);
}
double query(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].sums;
spread(p);
int mid=(b[p].l+b[p].r)>>1;
double ans=0;
if(l<=mid) ans+=query(p*2,l,r);
if(r>mid) ans+=query(p*2+1,l,r);
return ans;
}
int main()
{
n=read();
fr(i,1,n) a[i]=read();
build(1,1,n);
m=read();
fr(i,1,m)
{
int op=read(),l=read(),r=read(),v;
if(op==1)
{
v=read();
change(1,l,r,v);
}
if(op==2)
printf("%.1lf\n",query(1,l,r));
}
return 0;
}
树状数组+二分
HDU2852
思路:二分树状数组的位置
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
using namespace std;
const int MAX=100000+10;
int c[MAX];
int num[MAX];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int lowbit(int x){
return x&(-x);
}
void Update(int x,int d){
while(x<=MAX){
c[x]+=d;
x+=lowbit(x);
}
}
int Query(int x){
int sum=0;
while(x>0){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
int main(){
int m;
while(scanf("%d",&m)!=EOF){
int cnt=0;
memset(c,0,sizeof(c));
memset(num,0,sizeof(num));
while(m--){
int op=read();
if(op==0){
int x;
scanf("%d",&x);
Update(x,1);num[x]++;
cnt++;
}
else if(op==1){
int x;
x=read();
if(num[x]==0){printf("No Elment!\n");}
else {
Update(x,-1);
num[x]--;cnt--;}
}
else {
int a,k;
a=read(),k=read();
int res=Query(a);
if(res+k>cnt)printf("Not Find!\n");
else {
int l=a,r=MAX,ans;
while(l<=r){
int mid=(l+r)/2;
if(Query(mid)-res>=k)r=mid-1,ans=mid;
else l=mid+1;
}
printf("%d\n",ans);
}
}
}
}
return 0;
}
买礼物
思路:pre 表示第i个礼物的上一个相同礼物的位置
next表示第ii个礼物的下一个相同礼物的位置
我们要维护的就是区间 l ~r 间next[i] 的最小值或者pre[i]的最大值
我这里维护的是next[i]的最小值,当删除位置i的物品时,把next[i]置成n+1,查询时看区间最小值小于等于r就好
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
#define debug() cout<<"fuck "<<endl;
#define fr(i,k,n) for(int i=k;i<=n;i++)
#define fo(i,k,n) for(int i=n;i>=k;i--)
const int mod = 1e9+7,N=5e5+5,INF=0x3f3f3f3f;
const double eps=1e-6;
int n,m,k;
int pre[N],nx[N],a[N];
struct node
{
int l,r;
int minx; ///维护与a[l]相等的最小后继下标
}b[N<<2];
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&& ch<='9') {x=x*10+ch-'0';ch=getchar();}
return f*x;
}
void build(int p,int l,int r)
{
b[p].l=l,b[p].r=r;
if(l==r)
{
b[p].minx=nx[l];
return ;
}
int mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
b[p].minx=min(b[p*2].minx,b[p*2+1].minx);
}
void change(int p,int k,int pos)
{
if(b[p].l==b[p].r)
{
b[p].minx=pos;
return ;
}
int mid=(b[p].l+b[p].r)>>1;
if(k<=mid) change(p*2,k,pos);
else change(p*2+1,k,pos);
b[p].minx=min(b[p*2].minx,b[p*2+1].minx);
}
int ask(int p,int l,int r)
{
if(l<=b[p].l&&b[p].r<=r)
return b[p].minx;
int mid=(b[p].l+b[p].r)>>1;
int minx=INF;
if(l<=mid) minx=min(minx,ask(p*2,l,r));
if(r>mid) minx=min(minx,ask(p*2+1,l,r));
return minx;
}
map<int,int>mp;
int main()
{
n=read(),m=read();
fr(i,1,n) a[i]=read();
for(int i=1;i<=n;i++) ///找到和a[i]相等的前驱和后继下标
{
if(mp[a[i]]) pre[i]=mp[a[i]];
else pre[i]=0;
mp[a[i]]=i;
}
mp.clear();
for(int i=n;i>=1;i--)
{
if(mp[a[i]]==0) nx[i]=n+1;
else nx[i]=mp[a[i]];
mp[a[i]]=i;
}
build(1,1,n);
for(int i=1;i<=m;i++)
{
int op=read(),l=read(),r;
if(op==1)
{
change(1,l,n+1);
change(1,pre[l],nx[l]);
nx[pre[l]]=nx[l];
pre[nx[l]]=pre[l];
}
if(op==2)
{
r=read();
int ans=ask(1,l,r);
if(ans<=r&&ans>=l)
puts("1");
else puts("0");
}
}
return 0;
}