今日闲来无事乱翻刷题清单,看见分块这个专题,想起最近频频听人提起,便心血来潮学一波。
区间分块十分好学,二十分钟就大概弄清楚基础操作。
主要应用于一些区间离奇修改,线段树不好写的情况。
核心思想是将长度为n的区间分割为长度为
n
\sqrt{n}
n的
n
\sqrt{n}
n区间。
写出构建、修改、查询三个函数就可以了。
区间反转
题意
一排n盏灯,初始都灭。按输入执行以下两种操作
- 操作指定一个区间[a, b],然后改变编号在这个区间内的灯的状态
- 指定一个区间[a, b],要求你输出这个区间内有多少盏灯是打开的
思路
要求支持区间反转和区间查询,在线操作。
因为区间修改会耗费大量时间,当整个
n
\sqrt{n}
n区间都被修改时,添加lazy标签,不进行单个灯的修改。
代码
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
const int maxm=1005;
int num,siz,belong[maxn];//分块数量和大小,表示i属于哪一块
bool d[maxn];
struct block
{
int l,r,light;
bool lazy;
block()
{
l=r=light=0;//第i块有多少灯在亮
lazy=0;//0表示还未反转
}
} bl[maxm];
inline int read()//快读挂
{
int x=0,f=1;//改动x%mod即可在读入时取模
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
inline void out(register int x)
{
if(x>=10)
out(x/10);
putchar(x%10+'0');
}
void build(int n)
{
siz=sqrt(n);
num=n/siz;
if(num%siz)
num++;
for(int i=1;i<=num;i++)
bl[i].l=(i-1)*siz+1,bl[i].r=i*siz;
bl[num].r=n;//特殊处理
for(int i=1;i<=n;i++)
belong[i]=(i-1)/siz+1;
}
void debug(int n)
{
printf("lazy:\t");
for(int i=1;i<=n;i++)
printf("%4d",bl[belong[i]].lazy);
printf("\n");
printf("d:\t");
for(int i=1;i<=n;i++)
printf("%4d",d[i]);
printf("\nans:\t");
for(int i=1;i<=n;i++)
printf("%4d",bl[belong[i]].light);
printf("\n****\n");
}
void modify(int l,int r)
{
if(belong[l]==belong[r])//在同一块内
{
for(int i=l;i<=r;i++)
{//有r-l+1盏灯,暴力修改
if(d[i])
{
if(!bl[belong[i]].lazy)//灯在亮且本块未标记修改
bl[belong[i]].light--;
else
bl[belong[i]].light++;
}
else{
// printf("???");
if(!bl[belong[i]].lazy)//灯灭且区间未标记修改
bl[belong[i]].light++;
else
bl[belong[i]].light--;//灯灭且区间标记已修改
}
d[i]^=1;//散装灯的状态还是要修改的...
}
// debug(5);
return;
}
//否则不在同一块
for(int i=l;i<=bl[belong[l]].r;i++)
{
if(d[i])
{
if(!bl[belong[i]].lazy)//灯在亮且本块未标记修改
bl[belong[i]].light--;
else
bl[belong[i]].light++;
}
else{
if(!bl[belong[i]].lazy)//灯灭且区间未标记修改
bl[belong[i]].light++;
else
bl[belong[i]].light--;//灯灭且区间标记已修改
}
d[i]^=1;
}
for(int i=belong[l]+1;i<belong[r];i++)//整块的修改lazy标记
{
bl[i].lazy^=1;
bl[i].light=siz-bl[i].light;
}
for(int i=bl[belong[r]].l;i<=r;i++)
{
if(d[i])
{
if(!bl[belong[i]].lazy)//灯在亮且本块未标记修改
bl[belong[i]].light--;
else
bl[belong[i]].light++;
}
else{
if(!bl[belong[i]].lazy)//灯灭且区间未标记修改
bl[belong[i]].light++;
else
bl[belong[i]].light--;//灯灭且区间标记已修改
}
d[i]^=1;
}
}
int query(int l,int r)
{
if(belong[l]==belong[r])//在同一块内
{
int cnt=0;
for(int i=l;i<=r;i++)
if(d[i]^bl[belong[i]].lazy)
cnt++;
return cnt;
}
int ans=0;//不在同一块
for(int i=l;i<=bl[belong[l]].r;i++)
if(d[i]^bl[belong[i]].lazy)
ans++;
for(int i=belong[l]+1;i<belong[r];i++)
ans+=bl[i].light;
for(int i=bl[belong[r]].l;i<=r;i++)
if(d[i]^bl[belong[i]].lazy)
ans++;
return ans;
}
int main()
{
int n,m,ope,a,b;
n=read(),m=read();
build(n);
while(m--)
{
ope=read(),a=read(),b=read();
if(ope==0)
modify(a,b);
else
out(query(a,b)),putchar('\n');
}
return 0;
}
区间众数
题意
给定区间,求出这段区间的众数,强制在线。
本题不爆int,但有点卡常,套了个快读板子……
做法
因为种类(所给的数)过大,要先离散化。
但可以通过map和记录id对应的val同样区分种类和相对大小,并且好写,我就这么干了QwQ。
其实是我太菜了不会离散化
同时开一个
v
e
c
t
o
r
vector
vector数组
p
o
s
pos
pos,
p
o
s
[
x
]
pos[x]
pos[x]记录编号为
x
x
x的蒲公英所有的出现位置,通过STL函数来二分查找一个区间内x出现的次数。
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示第i块到第j块的众数,在查询之前先预处理出来。
注意要一开始就更新
a
n
s
ans
ans和
m
a
x
c
maxc
maxc。
然后枚举询问
[
l
,
r
]
[l,r]
[l,r]左面和右面散装蒲公英的种类,在整个区间内查询该种类的数量,与
m
a
x
c
maxc
maxc进行比较并记录最大值即可。
在
q
u
e
r
y
query
query里面暴力写写写就好了。
AC的代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()//快读挂
{
int x=0,f=1;//改动x%mod即可在读入时取模
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
inline void out(register int x)
{
if(x>=10)
out(x/10);
putchar(x%10+'0');
}
const int maxn=40005,lim=205,inf=0x3f3f3f3f;
int a[maxn],c[maxn],id=0,f[lim][lim];
int siz,num,belong[maxn];
struct block
{
int l,r;
} b[lim];
map<int,int> QAQ;
vector<int> pos[maxn];//存储种类下标
int val[maxn];//QAQ的反函数
void debug(int n)
{
printf("siz=%d,num=%d\n",siz,num);
for(int i=1;i<=num;i++)
{
for(int j=i;j<=num;j++)
{
printf("f[%d][%d]=%d\n",i,j,f[i][j]);
}
}
}
void build(int n)
{
siz=sqrt(n);
num=n/siz;
if(n%siz)
num++;
for(int i=1;i<=num;i++)
b[i].l=(i-1)*siz+1,b[i].r=i*siz;
b[num].r=n;//特殊处理
for(int i=1;i<=n;i++)
belong[i]=(i-1)/siz+1;
//伪离散化
// for(int i=1;i<=n;i++)
// pos[i].push_back(-1);
for(int i=1;i<=n;i++)
{
if(QAQ[a[i]]==0)
{
QAQ[a[i]]=++id;//新编号
val[id]=a[i];
// printf("val[%d]=%d,QAQ[%d]=%d\n",id,val[id],a[i],QAQ[i]);
}
a[i]=QAQ[a[i]];
pos[a[i]].push_back(i);//记录出现位置,自动升序
}
//暴力预处理区间众数,f[i][j]表示i块到j块*众数编号*
for(int i=1;i<=belong[n];i++)
{
memset(c,0,sizeof(c));
int maxc=0,ans=0;
for(int j=b[i].l;j<=n;j++)//枚举起点,一直处理到最后
{
c[a[j]]++;//统计数量
if(c[a[j]]>maxc||c[a[j]]==maxc&&val[a[j]]<val[ans])
{//更新当前众数答案
maxc=c[a[j]];//更新众数数量
ans=a[j];
}
f[i][belong[j]]=ans;
// printf("a[%d]=%d,c=%d\n",j,a[j],c[a[j]]);
// printf("f[%d][%d]=%d\n",i,belong[j],f[i][belong[j]]);
}
}
// debug(n);
}
int querynum(int l,int r,int x)//查询[l,r]内编号为x的数量
{
return upper_bound(pos[x].begin(),pos[x].end(),r)-lower_bound(pos[x].begin(),pos[x].end(),l);
}
int query(int l,int r)
{
int ans=0,maxc=0;
ans=f[belong[l]+1][belong[r]-1];//提溜出来中间的整块众数
maxc=querynum(l,r,ans);
if(belong[l]==belong[r])
{
// memset(c,0,sizeof(c));
for(int i=l;i<=r;i++)
{
int num=querynum(l,r,a[i]);//查询a[i]在[l,r]上的个数
if(num>maxc||num==maxc&&val[a[i]]<val[ans])
{
// printf("ans=%d,maxc=%d\n",ans,maxc);
ans=a[i];
maxc=num;
}
}
return val[ans];
}
//不在同一块
// printf("ans=%d,maxc=%d\n",ans,maxc);
for(int i=l;i<=b[belong[l]].r;i++)//枚举左面散装
{
int num=querynum(l,r,a[i]);//查询a[i]在[l,r]上的个数
if(num>maxc||num==maxc&&val[a[i]]<val[ans])
{
// printf("ans=%d,maxc=%d\n",ans,maxc);
ans=a[i];
maxc=num;
}
}
for(int i=b[belong[r]].l;i<=r;i++)
{
int num=querynum(l,r,a[i]);//查询a[i]在[l,r]上的个数
if(num>maxc||num==maxc&&val[a[i]]<val[ans])
{
ans=a[i];
maxc=num;
}
// printf("a[%d]=%d,ans=%d,maxc=%d\n",i,a[i],ans,maxc);
}
// printf("ans=%d,maxc=%d\n",ans,maxc);
return val[ans];//返回实际种类
}
signed main()
{
// freopen("P4168.in","r",stdin);
int n,m,ans=0;//n株蒲公英,m次询问
// scanf("%d%d",&n,&m);
n=read(),m=read();
for(int i=1;i<=n;i++)
a[i]=read();
build(n);
while(m--)
{
int l0,r0,l,r;
// scanf("%d%d",&l0,&r0);
l0=read(),r0=read();
l=(l0+ans-1)%n+1;
r=(r0+ans-1)%n+1;
if(l>r)
swap(l,r);
ans=query(l,r);
printf("%d\n",ans);
}
return 0;
}
2019年12月4日22点48分