首页 > 其他分享 >洛谷P2801 教主的魔法之分块板子

洛谷P2801 教主的魔法之分块板子

时间:2024-08-01 16:41:27浏览次数:10  
标签:洛谷 bel 分块 int P2801 ans 身高 block

洛谷P2801题解

传送锚点

摸鱼环节

教主的魔法

题目描述

教主最近学会了一种神奇的魔法,能够使人长高。于是他准备演示给 XMYZ 信息组每个英雄看。于是 \(N\) 个英雄们又一次聚集在了一起,这次他们排成了一列,被编号为 \(1, 2, \ldots, N\)。

每个人的身高一开始都是不超过 \(1000\) 的正整数。教主的魔法每次可以把闭区间 \([L, R]\)(\(1≤L≤R≤N\))内的英雄的身高全部加上一个整数 \(W\)。(虽然 \(L=R\) 时并不符合区间的书写规范,但我们可以认为是单独增加第 \(L(R)\) 个英雄的身高)

CYZ、光哥和 ZJQ 等人不信教主的邪,于是他们有时候会问 WD 闭区间 \([L, R]\) 内有多少英雄身高大于等于 \(C\),以验证教主的魔法是否真的有效。

WD 巨懒,于是他把这个回答的任务交给了你。

输入格式

第 \(1\) 行为两个整数 \(N, Q\)。\(Q\) 为问题数与教主的施法数总和。

第 \(2\) 行有 \(N\) 个正整数,第 \(i\) 个数代表第 \(i\) 个英雄的身高。

第 \(3\) 到第 \(Q+2\) 行每行有一个操作:

  1. 若第一个字母为 M,则紧接着有三个数字 \(L, R, W\)。表示对闭区间 \([L, R]\) 内所有英雄的身高加上 \(W\)。

  2. 若第一个字母为 A,则紧接着有三个数字 \(L, R, C\)。询问闭区间 \([L, R]\) 内有多少英雄的身高大于等于 \(C\)。

输出格式

对每个 A 询问输出一行,仅含一个整数,表示闭区间 \([L, R]\) 内身高大于等于 \(C\) 的英雄数。

样例 #1

样例输入 #1

5 3
1 2 3 4 5
A 1 5 4
M 3 5 1
A 1 5 4

样例输出 #1

2
3

提示

【输入输出样例说明】

原先 \(5\) 个英雄身高为 \(1, 2, 3, 4, 5\),此时 \([1, 5]\) 间有 \(2\) 个英雄的身高大于等于 \(4\)。教主施法后变为 \(1, 2, 4, 5, 6\),此时 \([1, 5]\) 间有 \(3\) 个英雄的身高大于等于 \(4\)。

【数据范围】

对于 \(30\%\) 的数据,\(N≤1000\),\(Q≤1000\)。

对于 \(100\%\) 的数据,\(N≤10^6\),\(Q≤3000\),\(1≤W≤1000\),\(1≤C≤10^9\)。


简单分析下题目,大概就是一群相信科学的people想判断教主的魔法科不科学,在施法后查下身高。但自己又巨懒,于是你喜提一道oi题,所以,正解应该是让他们自己去量自己的身高。先看数据量,有点小多这英雄都这么普遍的吗?,所以我们选择分块。



正片开始

首先,很明显这是一道分块的板子题,所以我们直接考虑分块做法(绝不是我不会写其他的)。

1.创建分块

对于每个分块,我们需要维护分块大小(block), 分块数量(tot), 每一块的左右端点(l,r), 以及每一个点(i)的归属块(bel[i]), 由于题目的需要需要一个d数组copy一下进行排序处理。

一般来说,分块的大小为\(\sqrt{n}\) , 所以我们这里的block=\(\sqrt{n}\)(绝不是难的我不会),此时我们很容易得到块的数量为tot=n/block,当有剩余散块时,块数还应+1。

同样,块的左右边界也可以推理出来

\(L_{x}=(x-1)\cdot block+1\),\(R_{x}=x\cdot block\),另外\(R_{tot}=n\)。

code:

void build()
{
    block=sqrt(n);tot=n/block;
    if(n%block) tot++;
    for(int i=1;i<=tot;i++)
    {
        l[i]=(i-1)*block+1;
        r[i]=i*block;
    }
    r[tot]=n;
    for(int i=1;i<=n;i++) bel[i]=(i-1)/block+1,d[i]=a[i];
    for(int i=1;i<=tot;i++) sort(d+l[i],d+r[i]+1);
}

2.处理修改操作

  1. 首先我们处理区间[l,r]完全在一个块里的情况,此时直接暴力枚举加值即可。

  2. 考虑区间跨度超过1个块以上的情况,利用线段树的小技巧,我们可以很快的想到在整块区间打标记的做法。对于散块直接暴力枚举即可。

  3. 由于我们还需要要快速的求出答案,所以需要维护出一个有序的数组d。

code:

void change(int x,int y,int k)
{
    if(bel[x]==bel[y])
    {
        for(int i=x;i<=y;i++) a[i]+=k;
        for(int i=l[bel[x]];i<=r[bel[x]];i++) d[i]=a[i];
        sort(d+l[bel[x]],d+r[bel[x]]+1);
    }
    else
    { 
        for(int i=x;i<=r[bel[x]];i++) a[i]+=k;
        for(int i=l[bel[x]];i<=r[bel[x]];i++) d[i]=a[i];
        sort(d+l[bel[x]],d+r[bel[x]]+1);
        for(int i=l[bel[y]];i<=y;i++) a[i]+=k;
        for(int i=l[bel[y]];i<=r[bel[y]];i++) d[i]=a[i];
        sort(d+l[bel[y]],d+r[bel[y]]+1);
        for(int i=bel[x]+1;i<=bel[y]-1;i++) lazy[i]+=k;
    } 
}

3.处理查询操作

又到最美好的查答案阶段了,在这距离AC不远的地方,我们选择和修改一样的思路进行处理。

  1. 查询区间[l,r]在一个块里,直接暴戾求解答案,反正就\(\sqrt{n}\) 个数,简单分析可以发现复杂度完全可以接受。
  2. 对于多于一个块的区间,也是分成散块和整块,暴力求散,二分求整。

code:

void query(int x,int y,int k)
{
    int ans=0;
    if(bel[x]==bel[y])
    {
        for(int i=x;i<=y;i++) if(lazy[bel[x]]+a[i]>=k) ans++;
        cout<<ans<<endl;
        return;
    }
    else
    {
        for(int i=x;i<=r[bel[x]];i++) if(lazy[bel[x]]+a[i]>=k) ans++;
        for(int i=l[bel[y]];i<=y;i++) if(lazy[bel[y]]+a[i]>=k) ans++;
        for(int i=bel[x]+1;i<=bel[y]-1;i++)
        {
            int ll=l[i],rr=r[i],res=0,mid;
            while(ll<=rr)
            {
                mid=(ll+rr)>>1;
                if(d[mid]+lazy[i]>=k) rr=mid-1,res=r[i]-mid+1;
                else ll=mid+1;
            }
            ans+=res;
        }
        cout<<ans<<endl;
        
        return;
    }
}

主体到此就结束了。


完整代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,q,d[N],a[N],l[N],r[N],bel[N],lazy[N],ans;
int block,tot,x,y,k;
void build()
{
    block=sqrt(n);tot=n/block;
    if(n%block) tot++;
    for(int i=1;i<=tot;i++)
    {
        l[i]=(i-1)*block+1;
        r[i]=i*block;
    }
    r[tot]=n;
    for(int i=1;i<=n;i++) bel[i]=(i-1)/block+1,d[i]=a[i];
    for(int i=1;i<=tot;i++) sort(d+l[i],d+r[i]+1);
}
void change(int x,int y,int k)
{
    if(bel[x]==bel[y])
    {
        for(int i=x;i<=y;i++) a[i]+=k;
        for(int i=l[bel[x]];i<=r[bel[x]];i++) d[i]=a[i];
        sort(d+l[bel[x]],d+r[bel[x]]+1);
    }
    else
    { 
        for(int i=x;i<=r[bel[x]];i++) a[i]+=k;
        for(int i=l[bel[x]];i<=r[bel[x]];i++) d[i]=a[i];
        sort(d+l[bel[x]],d+r[bel[x]]+1);
        for(int i=l[bel[y]];i<=y;i++) a[i]+=k;
        for(int i=l[bel[y]];i<=r[bel[y]];i++) d[i]=a[i];
        sort(d+l[bel[y]],d+r[bel[y]]+1);
        for(int i=bel[x]+1;i<=bel[y]-1;i++) lazy[i]+=k;
    } 
}
void query(int x,int y,int k)
{
    int ans=0;
    if(bel[x]==bel[y])
    {
        for(int i=x;i<=y;i++) if(lazy[bel[x]]+a[i]>=k) ans++;
        cout<<ans<<endl;
        return;
    }
    else
    {
        for(int i=x;i<=r[bel[x]];i++) if(lazy[bel[x]]+a[i]>=k) ans++;
        for(int i=l[bel[y]];i<=y;i++) if(lazy[bel[y]]+a[i]>=k) ans++;
        for(int i=bel[x]+1;i<=bel[y]-1;i++)
        {
            int ll=l[i],rr=r[i],res=0,mid;
            while(ll<=rr)
            {
                mid=(ll+rr)>>1;
                if(d[mid]+lazy[i]>=k) rr=mid-1,res=r[i]-mid+1;
                else ll=mid+1;
            }
            ans+=res;
        }
        cout<<ans<<endl;
        
        return;
    }
}
int main()
{
	cin>>n>>q;
    for(int i=1;i<=n;i++) cin>>a[i];
    build();
    while(q--)
    {
        char op;cin>>op;
        int x,y,z;cin>>x>>y>>z;
        if(op=='M')change(x,y,z);
        else query(x,y,z);
    }
	return 0;
}


code完成

谢谢观赏

标签:洛谷,bel,分块,int,P2801,ans,身高,block
From: https://www.cnblogs.com/qc0817/p/18336959

相关文章

  • 洛谷 B3612 【深进1.例1】求区间和
    "这道题也太水了吧,模拟就行了!""数据范围...""好像不行呀""呜呜~~TLE!"献上暴力代码!#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+5;intn,a[N],m;signedmain(){ios::sync_with_stdio(0);cin.tie(0);......
  • 洛谷题单指南-前缀和差分与离散化-P4552 [Poetize6] IncDec Sequence
    原题链接:https://www.luogu.com.cn/problem/P4552题意解读:对一组数字序列,进行若干次区间+1或者-1操作,最终使得所有数字一样,计算最少的操作次数,以及能得到多少种不同序列。解题思路:要使得序列每一个数字都相同,则其差分除了第一项之外其余项都是0。因此,问题转化为:给定一个差分数......
  • 洛谷题单指南-前缀和差分与离散化-P2882 [USACO07MAR] Face The Right Way G
    原题链接:https://www.luogu.com.cn/problem/P2882题意解读:一个有F、B组成的序列,每次可以翻转k个连续子序列,翻转:F->B,B->F,计算最少翻转多少次可以将序列都变成F,并求相应的k。解题思路:为方便处理,设F为1,B为01、朴素做法枚举k:1~n  枚举序列,一旦遇到0,就将连续k个字符翻转,如果可......
  • 洛谷P2696之慈善的约瑟夫——题解
    洛谷P2696题解[传送锚点](P2696慈善的约瑟夫-洛谷|计算机科学教育新生态(luogu.com.cn))比不过大佬,从蒟蒻做起选择比较水有意思的解法——用队列模拟(还是窝太弱了)正片开始考虑队列模拟,因为每次都是假的剔除,所以我们的目标是找到每回游戏的最终存活者。将不被剔除,......
  • 将 HTTP 分块编码数据流代码片段从 Node.js 转换为 Python
    我有一个Node.js客户端代码,它将请求发送到HTTP服务器,然后连续接收分块编码数据。这是带有一些流量数据输出的Node.js代码。consthttp=require('http');constoptions={hostname:'...',path:'...',port:...,...};constreq=http.request(......
  • 洛谷-P1171-售货员的难题
    Abstract传送门也算是状压dp模板题?不过这个数据给的有点阴间了,空间不够用,需要搞一个奇妙的优化。idea所谓状压,就是用数字表示当前状态,比如说0110100这个数字,我们可以把01分别看作是是否到达过第i个点的标记。那么我们可以用dp[i][j]表示第i个状态下,快递员到达j......
  • Contest7506 - 莫队 分块
    ContestA至少重复三次的数字莫队板子。B小明的习题集洛谷原题P1494[国家集训队]小Z的袜子。C棋子的颜色洛谷原题P1903[国家集训队]数颜色/维护队列。带修莫队:普通莫队是二维问题,现在加上一维时间轴,做法基本上同普通莫队。对询问\((l,r,t)\)排序时,第一关键......
  • 分块矩阵方法
    分块矩阵法是高等代数中处理矩阵相关问题的最重要的方法之一,其重要性可以说怎么强调都不过分.分块矩阵法的核心思想是根据具体问题构造适当的分块矩阵,然后运用广义初等变换,将某些子块消为零块,得到特殊的分块矩阵从而解决问题.该方法几乎贯穿了线性代数的始终,在矩阵求......
  • 洛谷题单指南-前缀和差分与离散化-P1083 [NOIP2012 提高组] 借教室
    原题链接:https://www.luogu.com.cn/problem/P1083题意解读:已知第i天有r[i]个教室可以供租借,有m个租借教室的订单,第i订单需要在第s[i]~t[i]天区间内租借d[i]个教室,计算是否全部订单都能满足,如果不满足要输出从第几个订单开始不满足。解题思路:1、朴素做法枚举1~m个订单,通过差分......
  • 洛谷题单指南-前缀和差分与离散化-P3406 海底高铁
    原题链接:https://www.luogu.com.cn/problem/P3406题意解读:1-n个城市共了n段路,第i段路不买卡票价a[i],买卡票价b[i](卡本身花费c[i]),给定一个路程顺序,计算最少的通行费用。解题思路:根据路程,计算每段路各走了多少次,然后对于每段路,计算买卡和不买卡两种花费,取较小的累加即可。如何......