zoukankan      html  css  js  c++  java
  • 数列分块入门九题(二):LOJ6280~6282

    Preface

    个人感觉这中间的三题是最水的没有之一


    数列分块入门 4——区间加法,区间求和

    这个也是很多数据结构完爆的题目线段树入门题,但是练分块我们就要写吗

    修改还是与之前类似,只不过我们要维护每一块内元素的和,注意这个要实时更新

    这样就可以轻松水过了。

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cmath>
    using namespace std;
    const int N=50005,BLO=250;
    int n,a[N],blk[N],size,opt,x,y,z;
    long long mark[BLO],sum[BLO];
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    inline int min(int a,int b)
    {
    	return a<b?a:b;
    }
    inline int query(int l,int r,int mod)
    {
    	register int i; int res=0;
    	for (i=l;i<=min(blk[l]*size,r);++i) res=(res+1LL*(a[i]+mark[blk[l]]))%mod;
    	if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) res=(res+1LL*(a[i]+mark[blk[r]]))%mod;
    	for (i=blk[l]+1;i<=blk[r]-1;++i) res=(res+1LL*sum[i])%mod;
    	return res;
    }
    inline void modify(int l,int r,int x)
    {
    	register int i;
    	for (i=l;i<=min(blk[l]*size,r);++i)
    	a[i]+=x,sum[blk[l]]+=x;
    	if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i)
    	a[i]+=x,sum[blk[r]]+=x;
    	for (i=blk[l]+1;i<=blk[r]-1;++i)
    	mark[i]+=x,sum[i]+=x*size;
    }
    int main()
    {
    	//freopen("4.in","r",stdin); freopen("4.out","w",stdout);
    	register int i; read(n); size=(int)sqrt(n);
    	for (i=1;i<=n;++i)
    	read(a[i]),sum[blk[i]=(i-1)/size+1]+=a[i];
    	for (i=1;i<=n;++i)
    	{
    		read(opt); read(x); read(y); read(z);
    		if (opt) write(query(x,y,z+1)),putchar('
    '); else modify(x,y,z);
    	}
    	return 0;
    }
    

    数列分块入门 5——区间开方,区间求和

    这道题其实也是一道经典的并查集的题目,但是我们只讲分块。

    首先我们要注意到,一个数被进行开方操作至多(O(log^2n))次时它就会变成(0/1)

    然后我们对于每一个块在维护和的同时,在打上一个标记,当一个块内所有元素都是(0/1)时就不更新它。

    否则暴力搞一遍即可。最后注意修改时记得把区间和的标记一起维护不能直接开方

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cmath>
    using namespace std;
    const int N=50005,BLO=250;
    int n,a[N],blk[N],sum[BLO],size,opt,x,y,z;
    bool flag[BLO];
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    inline int min(int a,int b)
    {
    	return a<b?a:b;
    }
    inline void reset(int x)
    {
    	register int i; flag[x]=1;
    	for (i=(x-1)*size+1;i<=x*size;++i)
    	{
    		sum[x]-=a[i]; a[i]=sqrt(a[i]); sum[x]+=a[i];
    		if (a[i]>1) flag[x]=0;
    	}
    }
    inline int query(int l,int r)
    {
    	register int i; int res=0;
    	for (i=l;i<=min(blk[l]*size,r);++i) res+=a[i];
    	if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) res+=a[i];
    	for (i=blk[l]+1;i<=blk[r]-1;++i) res+=sum[i];
    	return res;
    }
    inline void modify(int l,int r)
    {
    	register int i;
    	for (i=l;i<=min(blk[l]*size,r);++i) 
    	sum[blk[l]]-=a[i],a[i]=sqrt(a[i]),sum[blk[l]]+=a[i];
    	if (blk[l]!=blk[r]) for (i=(blk[r]-1)*size+1;i<=r;++i) 
    	sum[blk[r]]-=a[i],a[i]=sqrt(a[i]),sum[blk[r]]+=a[i];
    	for (i=blk[l]+1;i<=blk[r]-1;++i) if (!flag[i]) reset(i);
    }
    int main()
    {
    	//freopen("5.in","r",stdin); freopen("5.out","w",stdout);
    	register int i; read(n); size=sqrt(n);
    	for (i=1;i<=n;++i)
    	read(a[i]),sum[blk[i]=(i-1)/size+1]+=a[i];
    	for (i=1;i<=n;++i)
    	{
    		read(opt); read(x); read(y); read(z);
    		if (opt) write(query(x,y)),putchar('
    '); else modify(x,y);
    	}
    	return 0;
    }
    

    数列分块入门 6——单点插入,单点询问

    首先要正确理解题意,然后我们发现这个和分块有个毛线关系。

    我们注意到对于使用数组模拟这个过程时,查询是(O(1))的,但在同时插入时是(O(n))

    那么我们思考如何权衡这个问题,我们也可以进行分块。

    只不过这里的分块就是对于每块内开一个vector,然后你要知道有一个insert函数真是超级好用

    然后我们在插入时可以做到(O(sqrt n)),在数据随机的情况下表现优异。

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cmath>
    #include<vector>
    #define pb push_back
    using namespace std;
    const int N=200005,BLO=450;
    int n,size,opt,x,y,z,tot;
    vector <int> r[BLO];
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    inline int min(int a,int b)
    {
    	return a<b?a:b;
    }
    inline int query(int x)
    {
    	for (register int i=1;i<=tot;++i)
    	if (x<=r[i].size()) return r[i][x-1]; else x-=r[i].size();
    }
    inline void insert(int x,int y)
    {
    	for (register int i=1;i<=tot;++i)
    	if (x<=r[i].size()) { r[i].insert(r[i].begin()+x-1,y); return; } else x-=r[i].size();
    }
    int main()
    {
    	//freopen("a.in","r",stdin); freopen("a.out","w",stdout);
    	register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
    	for (i=1;i<=n;++i)
    	read(x),r[(i-1)/size+1].pb(x);
    	for (i=1;i<=n;++i)
    	{
    		read(opt); read(x); read(y); read(z);
    		if (opt) write(query(y)),putchar('
    '); else insert(x,y);
    	}
    	return 0;
    }
    

    不过上面的做法也有一定的缺陷,如果出题人就是要卡你,只需要一直出在同一位置插入的数据即可。

    然后最坏情况下就变成(O(n^2))暴力了,然后我们引进重新分块这样的概念

    一种重构类似于替罪羊树式重构,当一个块内的元素特别多(可以设临界值)时,直接暴力把这个块裂成两半即可。

    但是我更喜欢一个超级粗暴的方式:每做(sqrt n)次操作后,直接重新分块(全部for过去),复杂度和大体的一致,也是(O(nsqrt n))的。

    这样就卡不了你了但是在随机数据下被不重构的分块吊打了

    CODE

    #include<cstdio>
    #include<cctype>
    #include<cmath>
    #include<vector>
    #define pb push_back
    using namespace std;
    const int N=200005,BLO=450;
    int n,size,opt,cur[N],x,y,z,tot,q;
    vector <int> r[BLO];
    inline char tc(void)
    {
    	static char fl[100000],*A=fl,*B=fl;
    	return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
    }
    inline void read(int &x)
    {
    	x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1;
    	while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
    }
    inline void write(int x)
    {
    	if (x>9) write(x/10);
    	putchar(x%10+'0');
    }
    inline int min(int a,int b)
    {
    	return a<b?a:b;
    }
    inline void rebuild(void)
    {
    	register int i,j; n=0;
    	for (i=1;i<=tot;++i)
    	{
    		for (j=0;j<r[i].size();++j)
    		cur[++n]=r[i][j]; r[i].clear();
    	}
    	size=sqrt(n); tot=(n-1)/size+1;
    	for (i=1;i<=n;++i)
    	r[(i-1)/size+1].pb(cur[i]);
    }
    inline int query(int x)
    {
    	for (register int i=1;i<=tot;++i)
    	if (x<=r[i].size()) return r[i][x-1]; else x-=r[i].size();
    }
    inline void insert(int x,int y)
    {
    	for (register int i=1;i<=tot;++i)
    	if (x<=r[i].size()) { r[i].insert(r[i].begin()+x-1,y); return; } else x-=r[i].size();
    }
    int main()
    {
    	//freopen("6.in","r",stdin); freopen("6.out","w",stdout);
    	register int i; read(n); size=sqrt(n); tot=(n-1)/size+1;
    	for (i=1;i<=n;++i)
    	read(x),r[(i-1)/size+1].pb(x);
    	for (q=n,i=1;i<=q;++i)
    	{
    		read(opt); read(x); read(y); read(z);
    		if (opt) write(query(y)),putchar('
    '); else insert(x,y);
    		if (i%size==0) rebuild();
    	}
    	return 0;
    }
    
  • 相关阅读:
    typeof 和 Object.prototype.toString 的区别
    获取地理信息的JavaScript 库 -- YQL Geo
    关于html5手机
    我看过的书的示例网站
    解决跨浏览器问题网站收集
    【docker】docker初试与填坑
    sunJCE or ibmJce,was服务器下使用des的注意点
    cxf-webservice-在was6服务器上运行
    微星b85(b85i b85-gaming) 系列dsdt
    IE10的bug?disabled button如何触发事件
  • 原文地址:https://www.cnblogs.com/cjjsb/p/9387565.html
Copyright © 2011-2022 走看看