标签:print read 格式 upd turn 怎么 就会 tchar 细节
分块就是乱搞(确信
分块本质就是优雅的暴力,通过预处理和根号平衡(玄学地)让复杂度降低
比如我们考虑一个线段树裸题:
区间加,区间查询,\(n<=1e5\)
显然暴力的做法是\(n^2\)的,那么我们有没有什么优化方法呢?
我们可以将整个序列分为若干块,提前预处理出每个块的和,每次修改如果包含一个整块就直接打标记,非整块范围就暴力修改
假设我们将\(k\)个元素分成一块,那么将一共分出\(\frac{n}{k}\)个块
对于修改和查询操作:整块\(O(1)\)标记或查询,散块暴力修改,那么一次操作复杂度最高为\(O(\frac{n}{k}+k)\)
根据均值不等式,当\(\frac{n}{k}=k\)时,\(\frac{n}{k}+k\)最小,也就是说当\(k=\sqrt{n}\)时总复杂度最低为\(O(n\sqrt{n})\)
虽然相比\(n^2\)很优秀,但是为啥我不写线段树呢???
众所周知,线段树维护的信息需要能够区间快速合并,但是分块由于过于暴力,可以忽略这个条件
如这道例题:
区间加,区间小于\(k\)的个数
线段树已经去世,但是分块仍然可以胜任:
预先将每个块内数字排序,对于修改操作,显然不会影响整块的有序性,对于散块我们可以暴力重构,复杂度\(O(\sqrt{n}logn)\)
对于查询操作,可以散块暴力查询,整块二分查找,复杂度\(O(\sqrt{n}logn)\)
总复杂度\(O(n\sqrt{n}logn)\)
分块牺牲了部分复杂度,所以处理信息更加灵活,保留的信息量更多
没什么好说的了,放代码康康格式吧
展开查看
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
int x=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=0,ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
int n,cnt;
int a[100010];
int bel[100010],tag[100010];
inline void update(int l,int r,int k)
{
int lx=bel[l],rx=bel[r];
for(int i=l;bel[i]==lx&&i<=r;++i)
a[i]+=k;
if(lx==rx) return ;
for(int i=r;bel[i]==rx;--i)
a[i]+=k;
for(int i=lx+1;i<rx;++i)
tag[i]+=k;
}
signed main()
{
n=read();
cnt=sqrt(n);
for(int i=1;i<=n;++i)
{
a[i]=read();
bel[i]=(i-1)/cnt+1;
}
for(int t,x,y,z,i=1;i<=n;++i)
{
t=read(),x=read(),y=read(),z=read();
if(!t)
update(x,y,z);
else
printf("%lld\n",a[y]+tag[bel[y]]);
}
return 0;
}
刚才讲过惹\(qwq\)
\(emmm\)和上一题方法一样,懒得讲了
也讲过了……(这样看来这篇博客画风清奇
这个有点意思
首先区间求和肯定要预处理出块内和,那对于区间开方怎么处理呢?
这个题目有个条件,\(a_i<=maxint\),我们可以算出对于每个数字最多开方\(6\)次就会变成\(1\)或\(0\),然后再也不会变化了
所以如果一个块内全部变为了\(1\)或者\(0\),就不用再处理了,那么对于块内存在非\(1\)或\(0\)的块,暴力重构,对于已经不会变化的块直接打\(continue\)标记然后跳过
考虑到数据随机,对每个块开个链表,记录下每个块内有目前多少个元素,然后模拟就好了
如果数据不随机怎么办?我们可以设置一个值s,每当插入s个数字就对整个序列重新分块,可以证明理论复杂度仍然是\(O(n\sqrt{n})\)级别的
这不线段树\(2\)吗(雾
我在这里再说明一下标记顺序的问题:一定要先乘再加!
先加后乘效果如下:
假设当前状态为\((a+add)*mul\),又添加了一组\([add2,mul2]\)标记
当前状态变为\(((a+add)*mul+add2)*mul2\)
展开\(((a+add)*mul*mul2+add2*mul2)\)
继续展开\((a*mul*mul2+add*mul*mul2+add2*mul2)\)
恢复标记格式:\((a+add+(\frac{add2}{mul}))*mul*mul2\)
发现了什么?分数!这样可能会丢精度\(qwq\)
如果我们先乘后加呢
\((a*mul+add)添加新标记\)[add2,mul2]$
当前状态变为\(((a*mul)+add)*mul2+add2\)
展开\((a*mul*mul2+add*mul2+add2)\)
恢复标记格式:\((a*mul*mul2)+add*mul2+add2\)
完美
注意细节:对散块处理之前要下放整个散块标记
似乎可以沿用分块入门\(2\)的套路,不过很不幸被卡了
考虑入门\(5\)的分析方法:对于非同一数字块暴力计算,对于同一数字块
……好像有点锅,先咕咕咕了
标签:print read 格式 upd turn 怎么 就会 tchar 细节
原文地址:https://www.cnblogs.com/knife-rose/p/12021693.html