分块——优化的暴力

对于很多的题目,我们都可以找到n^2的暴力算法。

但是,当n在10000到200000之间的时候,n^2基本稳稳卡掉。

发现,这样的题目,经常还与区间有关系的时候,可以考虑分块做法。

分块,顾名思义,就是把待处理的整个大区间分成若干块。

口诀是:块外暴力,块内查表。

那么这个块的大小应该怎么分呢??

应该是sqrt(n)大小。

证明:*&%&*^$#()&^%#$^&#$(^$%(并不会证)

自我感觉,分块很巧妙的把各种复杂度都向sqrt(N)靠近

发现很多的题,都是时间复杂度n根号n,空间复杂度也是n根号n

而且不管是什么操作,基本上都是根号n。既没有n^2,也不存在O(1)

感觉,巧妙地把复杂度平均了一下。

分块虽然是暴力,但是是一种非常有水平的暴力。

关键是状态的设计,怎样达到块外暴力,块内查表。

大概的感觉是,都要围绕块来进行设计。

而且,通常要有两个以上函数状态有机配合。。。

例题1:BZOJ 4241历史研究

Description:

一句话题意:求区间加权众数。

Solution:

f[i][j]表示从第i块开头到j的最大值 (查询的时候,块内查表)
cnt[i][j]表示从第i块开始到序列末尾j出现了多少次(便于暴力中的后缀差分)
边角余料处理一下就好啦~

Code:

一般情况下,对于块的左右端点有两种求法。

1.lower_bound(blo+1,blo+n+1,blo[x])块的左端点。

lower_bound(blo+1,blo+n+1,blo[x]+1)块的右端点+1

2.处理块的时候,直接结构体记录块的左右端点。(推荐)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100050;
const int B=333;
ll f[B][N],cnt[B][N];
int a[N],b[N],tot;
int num[N],sta[N],top;
int blo[N],BLO;
int n,m;
ll ans;
int main()
{
    scanf("%d%d",&n,&m);
    BLO=sqrt(n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),blo[i]=(i-1)/BLO+1,b[i]=a[i];
    sort(b+1,b+n+1);tot=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+n+1,a[i])-b;
    for(int i=1;i<=blo[n];i++){
        ll now=0;
        for(int j=lower_bound(blo+1,blo+n+1,i)-blo;j<=n;j++){
            cnt[i][a[j]]++,now=max(now,(ll)cnt[i][a[j]]*b[a[j]]),f[i][j]=now;
        }
    }
    int xx,yy;
    while(m--){
        scanf("%d%d",&xx,&yy);ans=f[blo[xx]+1][yy];
        int tmp=lower_bound(blo+1,blo+n+1,blo[yy])-blo;top=0;
        for(int i=tmp;i<=yy;i++) num[a[i]]++,sta[++top]=a[i];
        tmp=lower_bound(blo+1,blo+n+1,blo[xx]+1)-blo;
        for(int i=xx;i<tmp;i++){
            num[a[i]]++;sta[++top]=a[i];
            ans=max(ans,(ll)((ll)num[a[i]]+cnt[blo[xx]+1][a[i]]-cnt[blo[yy]][a[i]])*b[a[i]]);
        }
        for(int i=1;i<=top;i++)num[sta[i]]=0;
        printf("%lld\n",ans);
    }
    return 0;
}

例题2:[HNOI2010]弹飞绵羊

Description:

某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。

Solution:

(LCT裸题,我们不管这个,因为我不会)

发现,直接枚举是n^2的。但是,许多的位置出发,可能会走到同一个位置,再跳出去。

类似于记忆化的思想。

假如设f[i]表示,从i开始,跳f[i]步出去。预处理倒序O(n)即可

查询是O(1)的。

但是一旦修改了,就会整个前面的f都会变。就凉凉了。。

要是修改只改一部分就好了。

即使查询不是O(1)也没关系啊。

分块,就来把这两个复杂度平均一下,都变成sqrt(n)

维护f[i],g[i]
f[i] 表示跳几次可以跳出所在块
g[i] 表示跳出所在块后到达的位置。
在查询时,我们O(sqrt(n))的时间进行“整块”的模拟,可以得到结果。

这样,我们以块为单位,出了块就交给别人去管就可以了。

f,g的巧妙搭配,使得查询只需要模拟即可!!

而且,修改时,只有块内收到了影响。暴力倒序修改即可。

Code:

#include<bits/stdc++.h>
using namespace std;
const int N=200000+10;
int n,m;
int BLO,blo[N];
int f[N],g[N];
int a[N];
int ans;
struct node{
    int l,r;
}kua[N];
int main()
{
    scanf("%d",&n);BLO=sqrt(n);
    for(int i=1;i<=n;i++) {
    scanf("%d",&a[i]),blo[i]=(i-1)/BLO+1;
    if(!kua[blo[i]].l) kua[blo[i]].l=i;
    kua[blo[i]].r=i;
    }
    for(int i=1;i<=blo[n];i++){
        for(int j=kua[i].r;j>=kua[i].l;j--){
            int to=j;
            while(to<=kua[i].r){
                if(!f[to]) to=to+a[j],f[j]++;
                else f[j]+=f[to],to=g[to];
            }
            g[j]=to;
        }
    }
    scanf("%d",&m);
    int op,x,y;
    while(m--){
        scanf("%d%d",&op,&x);x++;
        if(op==1){
            ans=0;
            int to=x;
            while(to<=n){
                ans+=f[to];
                to=g[to];
            }
            printf("%d\n",ans);
        }
        else{
            scanf("%d",&y);
            a[x]=y;
            for(int i=kua[blo[x]].r;i>=kua[blo[x]].l;i--){
                f[i]=g[i]=0;
                int to=i;
                while(to<=kua[blo[x]].r){
                    if(!f[to]) to=to+a[i],f[i]++;
                    else f[i]+=f[to],to=g[to];
                }
                g[i]=to;
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Miracevin/p/9403620.html

时间: 2024-07-30 02:25:37

分块——优化的暴力的相关文章

D. Powerful array 莫队算法或者说块状数组 其实都是有点优化的暴力

莫队算法就是优化的暴力算法.莫队算法是要把询问先按左端点属于的块排序,再按右端点排序.只是预先知道了所有的询问.可以合理的组织计算每个询问的顺序以此来降低复杂度. D. Powerful array 典型的莫队算法题 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include

bzoj 2301 [HAOI2011]Problem b(莫比乌斯反演+分块优化)

题意:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. 1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000 思路:莫比乌斯反演,ans=solve(b/k,d/k)-solve((a-1)/k,d/k)-solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k) 代码1:超时. #include<iostream> #include&l

BZOJ 2301 [HAOI2011]Problem b (容斥+莫比乌斯反演+分块优化 详解)

2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MB Submit: 2096  Solved: 909 [Submit][Status][Discuss] Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input 第一行一个整数n,接下来n行每行五个整数,分别表示a.b.c.d.k Out

BZOJ 2301 Problem b(莫比乌斯反演+分块优化)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=37166 题意:对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. 思路:本题使用莫比乌斯反演要利用分块来优化,那么每次询问的复杂度降为2*sqrt(n)+2*sqrt(m).注意到 n/i ,在连续的k区间内存在,n/i=n/(i+k).所有对这连续的区间可以一次求出

HDU5780 gcd (BestCoder Round #85 E) 欧拉函数预处理——分块优化

分析(官方题解): 一点感想: 首先上面那个等式成立,然后就是求枚举gcd算贡献就好了,枚举gcd当时赛场上写了一发O(nlogn)的反演,写完过了样例,想交发现结束了 吐槽自己手速慢,但是发了题解后发现,这题连O(n)欧拉函数前缀和的都卡了,幸亏没交,还是太年轻 对于官方题解说sqrt(n)优化(其实就是n/(小于n一段数)结果是一样的,也不算什么分块),还是很简单的,做反演题的时候看到过很多,只是忘记了 如果不会请看这篇解题报告http://wenku.baidu.com/view/fbe2

c++分块算法(暴力数据结构)

快要noip了,该写些题解攒攒rp了(逃) 看到题解里那么多线段树啊,树状数组啊,本蒟蒻表示:这都是什么鬼东西? 在所有高级数据结构中,树状数组是码量最小的,跑的也基本是最快的,但理解很难,并且支持的操作很少:线段树的码量,相信写过线段树题的童鞋都亲身体验过这种恐怖(那些3min写完splay的巨佬不要d我),理解虽然简单,但一题调一辈子啊! 所以说到这里,本蒟蒻想表达什么呢? 分块大法吼啊! 有人会说:分块不是n√n的复杂度吗?怎么能跟nlogn的数据结构相提并论呢?或者说,分块在联赛中,有什

BZOJ 1257 余数之和sum(分块优化)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=46954 题意:f(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n,输入n, k,求f(n, k). 思路:n>k的部分都为k,直接判断即可.n < k时,k mod n = k - k / n * n,观察发现在一定的区间[lhs, rhs]内k/i的值不变.那么就可以直接分块了,  k/lhs * lhs + k

楼房重建(分块优化)

题目描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊地看着窗外发呆,数自己能够看到多少栋房子. 为了简化问题,我们考虑这些事件发生在一个二维平面上.小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度.如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的. 施工队的建造总共进行了M天.初始时,所有楼房都还没有开始建造

BZOJ 2301: [HAOI2011]Problem b(莫比乌斯反演 + 容斥原理 + 分块优化)

传送门 Problem 2301. – [HAOI2011]Problem b 2301: [HAOI2011]Problem b Time Limit: 50 Sec  Memory Limit: 256 MBSubmit: 3671  Solved: 1643[Submit][Status][Discuss] Description 对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数. Input