码迷,mamicode.com
首页 > 编程语言 > 详细

【算法】分块——教主的魔法&不勤劳的图书管理员

时间:2018-03-01 14:52:04      阅读:157      评论:0      收藏:0      [点我收藏+]

标签:分块   排序   一个   保存   sig   ret   数据   pre   namespace   

由不勤劳的图书管理员带入了分块的坑,深深地被其暴力与优雅所征服。分块的实质就是将暴力块状封装起来,一整块的部分就一整块处理,零碎的部分就怎么暴力怎么来。因为分块大小的原因,限制了零碎部分数据的数量级,所以复杂度得以保证。

1.教主的魔法:可以算得上是一个分块的板子题。对于每一个块内sort排序,保存id值。对于修改,块内的找到点暴力修改之后重新排序,一整个块的不改变相对大小关系,所以直接外部记录累加的值。查询也一样,块外暴力,块内二分查找。

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define maxn 2000000
#define maxb 1050
int n, q, B, sum[maxb], a[maxn], cnt[maxb];
struct node
{
    int v, id;
}c[maxb][maxb];

int read()
{
    int x = 0;
    char c;
    c = getchar();
    while(c < 0 || c > 9) c = getchar();
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar();
    return x;
}

bool cmp(node a, node b)
{
    return a. v < b.v;
}

void Vio_C(int b, int x, int w)
{
    c[b][x].v += w;
}

void Change(int L, int R, int W)
{
    int a = L / B, b = R / B;
    if(a == b)
    {
        for(int i = 1; i <= cnt[a]; i ++) 
            if(c[a][i].id >= L && c[a][i].id <= R) Vio_C(a, i, W);
            sort(c[a] + 1, c[a] + cnt[a] + 1, cmp);
    }
    for(int i = 1; i <= cnt[a]; i ++) 
        if(c[a][i].id >= L) Vio_C(a, i, W);
    sort(c[a] + 1, c[a] + cnt[a] + 1, cmp);
    for(int i = 1; i <= cnt[b]; i ++)
        if(c[b][i].id <= R) Vio_C(b, i, W);
    sort(c[b] + 1, c[b] + cnt[b] + 1, cmp);
    for(int i = a + 1; i < b; i ++)
        sum[i] += W;
}

int check(int b, int C)
{
    C -= sum[b];
    int ans = cnt[b] + 1, l = 1, r = cnt[b];
    while(l <= r)
    {
        int mid = (l + r) >> 1;
        if(c[b][mid].v >= C) ans = mid, r = mid - 1;
        else l = mid + 1;
    }
    return cnt[b] - ans + 1;
}

void Query(int L, int R, int C)
{
    int a = L / B, b = R / B;
    int ans = 0;
    if(a == b)
    {
        C -= sum[a];
        for(int i = 1; i <= cnt[a]; i ++)
            if(c[a][i].id >= L && c[a][i].id <= R && c[a][i].v >= C) ans ++; 
        printf("%lld\n", ans);
        return;
    }
    for(int i = 1; i <= cnt[a]; i ++)
           if(c[a][i].id >= L && c[a][i].v + sum[a] >= C) ans ++;
    for(int i = 1; i <= cnt[b]; i ++)
        if(c[b][i].id <= R && c[b][i].v + sum[b] >= C) ans ++;
    for(int i = a + 1; i < b; i ++)
        ans += check(i, C);
    printf("%lld\n", ans);
    return; 
}

signed main()
{
    n = read(), q = read();
    B = sqrt(n);
    for(int i = 1; i <= n; i ++)
    { 
        a[i] = read(); int block = i / B;
        c[block][++ cnt[block]].v = a[i];
        c[block][cnt[block]].id = i;
        if(((i / B) != (i + 1) / B) || i == n) 
            sort(c[block] + 1, c[block] + cnt[block] + 1, cmp);
    }
    for(int i = 1; i <= q; i ++)
    {
        char c; cin >> c; 
        int L = read(), R = read(), W = read();
        if(c == M) Change(L, R, W);
        else Query(L, R, W);
    }
    return 0;
}

2.不勤劳的图书管理员

这题首先注意到交换a,b的位置,只会影响到a,b之间的数。在一段区间里,a对杂乱值的贡献是多少?不难发现=在a之前且应当在a之后的书本杂乱值之和+v[a]*前面书本的个数。所以我们对每一个块使用两个树状数组,一个纪录个数,一个记录页数的前缀和,块内的利用树状数组快速查询,块外的暴力枚举计算即可。

因为此题代码是Kuai的(那个时候还不会写分块),所以就不贴代码啦。

【算法】分块——教主的魔法&不勤劳的图书管理员

标签:分块   排序   一个   保存   sig   ret   数据   pre   namespace   

原文地址:https://www.cnblogs.com/twilight-sx/p/8487856.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!