LibreOJ 6282,6284,6285 数列分块入门6,8,9 树状数组,分块,线段树等.

  • 分块6
  • 分块8
  • 分块9

终于结尾了.
诶?我定义的常量变成yuzu了?我不要可怜了?
可怜有剧毒!我再用可怜我要爆零了.
好吧其实是因为我研究了橘学.这里我推荐一下Citrus.

分块6

支持插入一个数,询问一个位置上的数是多少.

/*
每块开一个vector,插入的时候先询问x指向的块和位置,然后插入就可以了;
询问的时候从第一个vector开始按顺序减去每一块的元素的个数,返回一个pair,first表示所在块,second表示块内的位置.
不要忘记如果某一块插入了很多数字要把所有块推倒重构.
快读快写把我坑死了.
*/
#include //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  re0 x=0,f=1;rec c=gc();
  for (;!isdigit(c);c=gc()) ;//f^=c=='-';
  for (;isdigit(c);c=gc()) x=x*10+c-'0';
  return x;//*(f?1:-1);
  }
inline void read(rel &x){
  x=0;re0 f=1;rec c=gc();
  for (;!isdigit(c);c=gc()) ;//f^=c=='-';
  for (;isdigit(c);c=gc()) x=x*10+c-'0';
  x*=f?1:-1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  //if (x<0) x=-x,pc('-');
  re0 bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  }
}using namespace chtholly;
using namespace std;
#define bl(x) (((x)-1)/block+1)//Which block x belong
#define ls(x) (((x)-1)*block+1)//The left side of block x 
#define rs(x) (min(n,(x)*block))//The right side of block x

typedef pair<int,int> pii; 
const int yuzu=2e5;
typedef int fuko[yuzu|10];
vector<int> v[yuzu|10];
int n,block,num,top;
fuko a,tmp;

void buildblock()
{
re0 i;
block=sqrt(n),num=n/block+(n%block?1:0);
for (i=1;i<=n;++i) v[bl(i)].push_back(a[i]);
}

pii query(int x)
{
int now=1;
for (;x>v[now].size();) x-=v[now++].size();
return pii(now,x-1);
}

void push_down()//推倒重来
{
top=0;re0 i;
for (i=1;i<=num;v[i++].clear()) 
  for (int j:v[i]) tmp[++top]=j;
int newblock=sqrt(top);num=top/newblock+(top%newblock?1:0);
for (i=1;i<=top;++i) v[bl(i)].push_back(tmp[i]); 
}

void insert(int pos,int x)
{
pii now=query(pos);
v[now.first].insert(v[now.first].begin()+now.second,x);
if (v[now.first].size()>20*block) push_down();
}

int main()
{
re0 i;
scanf("%d",&n);
for (i=1;i<=n;++i) scanf("%d",&a[i]);
buildblock();
for (i=1;i<=n;++i)
  {
  re0 op,l,r,c;
  scanf("%d%d%d%d",&op,&l,&r,&c);
  if (op)
    {
    pii ans=query(r);
    printf("%d\n",v[ans.first][ans.second]);
    }
  else insert(l,r);
  }
}

分块8

区间查询等于c的数的个数,并同时将区间内的所有数改为c.
本题玄学就在于查询的同时进行了修改.
如果查询与修改分开,这题分块就过不了了.

/*
我们来看均摊的复杂度.
对于查询和修改,被修改成同一个数字的可以打上标记.
这样被修改的只有头尾两块.
如果你想让一次查询是O(n)的,必须要先用O(sqrt(n))的复杂度的修改去修改每一块,把所有的标记都破坏掉.
所以复杂度是O(sqrt(n))的.
但是如果查询和修改是分开的,每次只修改一个块内的数,然后查询[1,n],就炸了.
当然这题还是有非常多细节要注意的.
当被打上标记的块被l或者r断开了,必须要把标记下推.
我debug了好久,最后竟然A掉了.
*/
#pragma GCC optimize("inline,Ofast",3)
#include //Ithea Myse Valgulious
#define yuri puts("niconiconi")
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  re0 x=0,f=1;rec c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=x*10+c-'0';
  return x*(f?1:-1);
  }
inline void read(rel &x){
  x=0;re0 f=1;rec c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=x*10+c-'0';
  x*=f?1:-1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  re0 bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  }
}using namespace chtholly;
using namespace std;
#define bl(x) (((x)-1)/block+1) 
#define ls(x) (((x)-1)*block+1)
#define rs(x) (min(n,(x)*block))
const int yuzu=1e5;
typedef int fuko[yuzu|10];
fuko a,tag;int n,block,num;

void buildblock()//³õʼ»¯block
{
re0 i;
block=sqrt(n),num=n/block+(n%block?1:0);
}

void debug()
{
re0 i;
for (i=1;i<=n;++i) printf("%d ",a[i]);pl;
for (i=1;i<=num;++i) printf("%d ",tag[i]);pl; 
}

void push_down(int x)//标记下推(就是线段树的延迟标记),其实就是暴力修改,然后把标记清零.
{
if (tag[x])
  for (re0 i=ls(x);i<=rs(x);++i) a[i]=tag[x];
}

int query(int l,int r,int c)
{
re0 i,ans=0; 
push_down(bl(l));
for (i=l;i<=min(r,rs(bl(l)));++i)
  ans+=a[i]==c;//暴力
if (bl(r)!=bl(l))//暴力
  {
  push_down(bl(r));
  for (i=ls(bl(r));i<=r;++i) ans+=a[i]==c; 
  }
for (i=bl(l)+1;iif (!tag[i]) //没有标记,暴力 
    for (re0 j=ls(i);j<=rs(i);++j) ans+=a[j]==c;
  else 
    ans+=tag[i]==c?rs(i)-ls(i)+1:0;//有标记,O(1)回答. 
  }
return ans;
}

void update(int l,int r,int c) 
{
re0 i;
push_down(bl(l));
for (i=l;i<=min(r,rs(bl(l)));++i)
  a[i]=c;//暴力
tag[bl(l)]=0;
if (bl(r)!=bl(l))
  {
  push_down(bl(r));
  for (i=ls(bl(r));i<=r;++i) a[i]=c;//暴力
  tag[bl(r)]=0;
  }
for (i=bl(l)+1;i//打标记
  tag[i]=c;
}

int main()
{
int i;
scanf("%d",&n);
for (i=1;i<=n;++i) scanf("%d",&a[i]);
buildblock();
for (i=1;i<=n;++i) 
  {
  re0 l,r,c;
  scanf("%d%d%d",&l,&r,&c);
  printf("%d\n",query(l,r,c));
  update(l,r,c);
  //debug();
  }
}

分块9

询问区间最小众数.
这题只有询问,看似简单,实则也不简单.
洛谷上有p4168蒲公英,双倍经验了解一下.
以下不是分块9的代码,是蒲公英的.
lojAC代码里可以看到我的.

/*
我们先把这些数离散化一下,然后预处理.
预处理要处理两个玩意.
第一个是[l,r]内有多少个数x.
第二个是块[l,r]内的众数是多少.
第一个O(n),第二个O(n*sqrt(n)).
询问的时候先把ans处理成[belong(l)+1,belong(r)-1]块内的众数.
然后把l,r所在的部分遍历一下,与答案比较.
*/
#include //Ithea Myse Valgulious
#define yuri puts("niconiconi")
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
  re0 x=0,f=1;rec c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=x*10+c-'0';
  return x*(f?1:-1);
  }
inline void read(rel &x){
  x=0;re0 f=1;rec c=gc();
  for (;!isdigit(c);c=gc()) f^=c=='-';
  for (;isdigit(c);c=gc()) x=x*10+c-'0';
  x*=f?1:-1;
  }
template <typename mitsuha>
inline int write(mitsuha x){
  if (!x) return pc(48);
  if (x<0) x=-x,pc('-');
  re0 bit[20],i,p=0;
  for (;x;x/=10) bit[++p]=x%10;
  for (i=p;i;--i) pc(bit[i]+48);
  }
inline char fuhao(){
  rec c=gc();
  for (;isspace(c);c=gc());
  return c;
  }
}using namespace chtholly;
using namespace std;
#define bl(x) (((x)-1)/block+1)
#define ls(x) (((x)-1)*block+1)
#define rs(x) (min((x)*block,n))
const int yuzu=1e5;
typedef int fuko[yuzu|10];
#define all(x) x.begin(),x.end()
int n=read(),m=read(),block,num,sum[1500][1500];
vector<int> pos[yuzu|10];
fuko a,b,cnt;

int query(int l,int r,int x){return upper_bound(all(pos[x]),r)-lower_bound(all(pos[x]),l);}//询问[l,r]内x有多少个.

void buildblock(){
re0 i;
block=sqrt(n/log2(n));
sort(b+1,b+n+1);
num=unique(b+1,b+n+1)-b-1;
for (i=1;i<=n;++i) {
  a[i]=lower_bound(b+1,b+num+1,a[i])-b;//离散化并预处理每一个数出现的位置
  pos[a[i]].push_back(i);
  }
for (i=1;i<=bl(n);++i){//预处理区间[i,j]的众数
  memset(cnt,0,sizeof cnt);
  int ans=0;
  for (re0 j=ls(i);j<=n;++j){
    cnt[a[j]]++;
    if (cnt[a[j]]>cnt[ans]||cnt[a[j]]==cnt[ans]&&a[j]int main(){
re0 i,t;
for (i=1;i<=n;++i) a[i]=b[i]=read();
buildblock();
int lastans=0;
for (t=1;t<=m;++t){
  int l=read(),r=read(),ans,tmp;
  l=(l+lastans-1)%n+1,r=(r+lastans-1)%n+1;
  if (l>r) swap(l,r);
  ans=sum[bl(l)+1][bl(r)-1],tmp=query(l,r,ans);
  for (i=l;i<=min(rs(bl(l)),r);++i){
    int t=query(l,r,a[i]);
    if (t>tmp||t==tmp&&a[i]/*
    如果a[i]出现的次数大于已有的众数,或者出现次数相等但是a[i]比已知众数小的话就用a[i]更新ans.
    */
    }
  if (bl(l)!=bl(r)){
    for (i=ls(bl(r));i<=r;++i){
      int t=query(l,r,a[i]);
      if (t>tmp||t==tmp&&a[i]

Thank you.Yeah!

你可能感兴趣的:(分块,总结)