• [Luogu P2801]教主的魔法


    题意就是让我们维护一个数据结构,可以实现区间修改和区间查询多少个数大于等于给定值。这个据说线段树可以写但是我并没有想到qwq,于是我使用了“优雅的暴力”——分块。

    分块做法十分显然,我们维护大小为√n的块(√n的块根据均值不等式复杂度取得最小值),维护一个lazy标记,区间修改的话,如果修改的区间是包含整个块就直接对lazy标记操作,如果是半个块就直接暴力修改就行。而查询操作要是直接找和暴力没啥区别。注意到块的大小其实也就最多1000,所以考虑排序之后二分查找,这样就ok了。

    时间复杂度为O(q√(nlogn))。

    其实思路非常简单,但是分块这个东西比较玄学,稍稍一个符号改动说不定就可以多水好多数据,就像我提交记录有10、20、60、80、90最后AC。debug时间大约是一个多小时qwq。

    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<iostream>
    #include<vector>
    #define N 1000010
    using namespace std;
    int block[N],size,lazy[N],a[N],n,q;
    vector<int>b[10001];
    void reset(int x)
    {
        b[x].clear();
        for(int i = (x - 1) * size + 1;i <= min(x * size,n);i++) b[x].push_back(a[i]);//不能直接对原序列排序,拿一个备用序列排序
        sort(b[x].begin(),b[x].end());
    }
    void add(int l,int r,int k)
    {
        if(block[l] == block[r])//区间在一个块里直接暴力操作
        {
            for(int i = l;i <= r;i++) a[i] += k;
            reset(block[l]);
            return;
        }
        for(int i = block[l] + 1;i <= block[r] - 1;i++) lazy[i] += k;//整个块就对lazy标记操作
        for(int i = l;i <= block[l] * size;i++) a[i] += k;
        reset(block[l]);
        for(int i = r;i > (block[r] - 1) * size;i--) a[i] += k;
        reset(block[r]);
        return;
    }
    int query(int l,int r,int k)
    {
        int ret = 0;
        if(block[l] == block[r])
        {
            for(int i = l;i <= r;i++) if(a[i] + lazy[block[l]] >= k) ret++;
            return ret;
        }
        for(int i = block[l] + 1;i <= block[r] - 1;i++)
        {
            ret += b[i].end() - b[i].begin() - (lower_bound(b[i].begin(),b[i].end(),k - lazy[i]) - b[i].begin());//二分查找多少个数比(k - lazy[i])大。
        }
        for(int i = l;i <= block[l] * size;i++) if(a[i] + lazy[block[i]] >= k) ret++;
        for(int i = r;i > (block[r] - 1) * size;i--) if(a[i] + lazy[block[i]] >= k) ret++;
        return ret;
    }
    int main()
    {
        scanf("%d %d",&n,&q);
        size = (int)sqrt(n);
        for(int i = 1;i <= n;i++) 
        {
            scanf("%d",&a[i]);
            block[i] = (i - 1) / size + 1;
            b[block[i]].push_back(a[i]);
        }
        for(int i = 1;i <= block[n];i++) sort(b[i].begin(),b[i].end());
        while(q--)
        {
            int l,r,k;char op;
            cin >> op;
            scanf("%d%d%d",&l,&r,&k);
            if(op == 'A') printf("%d
    ",query(l,r,k));
            else add(l,r,k);
        }
        return 0;
    }
  • 相关阅读:
    org.apache.hadoop.security.AccessControlException: org.apache.hadoop.security.AccessControlException: Permission denied:
    linux iptables跟service 命令无效解决方法
    eclipse运行C++控制台不输出结果的解决办法
    C#操作mpp文件代码参考
    vs2010 开始执行(不调试) ctrl F5 出现任意键继续
    关于gcc、glibc和binutils模块之间的关系,以及在现有系统上如何升级的总结
    NUnit在Visual Studio 2010中的配置和使用
    【转】从CSDN的趣味题学C# 3.0
    gnome3程序图标下的文字如何改大
    使用autoconf、automake等来制作一个以源代码形式(.tar.gz)发布的软件、并可在执行configure时使用自定义参数
  • 原文地址:https://www.cnblogs.com/lijilai-oi/p/10991545.html
Copyright © 2020-2023  润新知