U597856 茁壮地长大吧! [蔚蓝档案] 题解
题面背景:啊啊啊啊啊亚津子太可爱了,公主你带我走吧,即使风餐露宿,只要看到你那天真无邪的笑容,看到你手中捧这的蔷薇,为师就要幸福死了!
蔷薇的话语亚津子自然是知道的,可是当她却假装不知道送给 \(sensei\) ,啊啊啊公主你太会了。
即使是呲牙的表情也令人怜爱。
啊,公主你带我走吧!
题面描述:
维护一个序列,进行区间减,区间加单峰等差数列,单点查询操作。
正片开始:
大家好,我喜欢暴力数据结构,所以我用分块 A 掉了这到题。
首先发现整个题最难的地方在于进行 区间加单峰等差数列 操作,因此,考虑一个可以维护复杂修改的数据结构。
看了一眼数据范围 \(2e5\) 。。。。
200 ms 。。。
\(n \sqrt n\) 大概是 $9 \times 10^7 $ 左右。
欸,貌似可过。
好,开始考虑如何维护。
如果您不是到分块是什么,请移步这篇博客
-
对于区间减操作。可以直接对于每个块维护一个 \(laz\) 记录减的值。
-
对于区间加单峰等差数列操作,考虑到可能是单调递增或单调递减的。
所以对于每个块可以维护 \(laz1\) 和 \(laz2\) 。
\(laz1\) 记录加递减数列时块的右端点应该加的值。
\(laz2\) 记录加递增数列时块的左端点应该加的值。
然后对于峰点所在块和两端修改不了整个块的散块进行暴力修改。
- 对于单点查询操作,直接下放其所在块所有标记,直接查询即可。
时间复杂度分析:
对于每个修改操作,最多修改 \(n \log_{n}\) 个整块,修改每个散块需要修改 \(n \log_{n}\) 次。
因此对于修改操作,时间复杂度为 \(O(n \log_{n})\)
对于每个查询操作,只会遍历查询的点所在的块,只会遍历 \(n \log_{n}\) 个元素,因此时间复杂度也是 \(O(n \log_{n})\) 的。
这样即可通过此题。
但是发现跑的飞慢,难道分块的潜能就到此为止了吗?
可恶! 可不要小看我们之间的羁绊啊!
我们充分发挥人类智慧,发现每次查询是并不需要下方标记。
我们可以通过查询点下标与块左右定点作差来直接获得它需要加的值。
再直接减去 \(laz\) 即可。
根本不需要下放标记。
这样我们每次查询的复杂度就能到达 \(O(1)\) !
然后取得了除匿名用户最优解的结果
代码
```cpp #includeint n;
int m;
int len;
int top;
ll a[N];
int L[M];
int R[M];
int id[N];
ll laz[M];
ll laz1[M];//从右往左递减,记录右端点。
ll laz2[M];//从左往右递减,记录左端点。
int tim1[M];
int tim2[M];
using namespace std;
namespace ly
{
namespace IO
{
#define SIZE (1<<20)
char in[SIZE],out[SIZE],p1=in,p2=in,p3=out;
#define getchar() (p1p2&&(p2=(p1=in)+fread(in,1,SIZE,stdin),p1p2)?EOF:p1++)
#define flush() (fwrite(p3=out,1,SIZE,stdout))
#define putchar(ch) (p3out+SIZE&&flush(),*p3++=(ch))
class Flush{public:~Flush(){flush();}}_;
template
inline void read(type &x)
{
x=0;bool flag(0);char ch=getchar();
while(!isdigit(ch)) flag^=ch
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
flag?x=-x:0;
}
template
inline void write(type x,bool flag=1)
{
x<0?x=-x,putchar('-'):0;static short Stack[50],top(0);
do Stack[++top]=x%10,x/=10;while(x);
while(top) putchar(Stack[top--]|48);
flag?putchar('\n'):putchar(' ');
}
#undef SIZE
#undef getchar
#undef putchar
#undef flush
}
}using namespace ly::IO;
inline void update_del(int l,int r,int k) // O(log(n))
{
if(id[l] == id[r])
{
for(int i = l;i <= r;i ++) a[i] -= k;
return;
}
for(int i = l;i <= R[id[l]];i ++) a[i] -= k;
for(int i = L[id[r]];i <= r;i ++) a[i] -= k;
for(int i = id[l] + 1;i <= id[r] - 1;i ++) laz[i] += k;
}
inline void update_add(int p,int k) // O(log(n))
{
int l = max(p - k + 1,1);
int r = min(p + k - 1,n);
if(id[l] == id[r])
{
for(int i = p - 1,jk = k - 1;i >= l;i --,jk --) a[i] += jk;
for(int i = p + 1,jk = k - 1;i <= r;i ++,jk --) a[i] += jk;
a[p] += k;
return;
}
a[p] += k;
for(int i = p - 1,jk = k - 1;i >= L[id[p]];i --,jk --) a[i] += jk;
for(int i = p + 1,jk = k - 1;i <= R[id[p]];i ++,jk --) a[i] += jk;
if(id[l] != id[p]) for(int i = l,jk = k - p + l;i <= R[id[l]];i ++,jk ++) a[i] += jk;
if(id[p] != id[r]) for(int i = r,jk = k - r + p;i >= L[id[r]];i --,jk ++) a[i] += jk;
for(int i = id[l] + 1;i <= id[p] - 1;i ++) laz1[i] += (k - (p - R[i])),tim1[i] ++;
for(int i = id[p] + 1;i <= id[r] - 1;i ++) laz2[i] += (k - (L[i] - p)),tim2[i] ++;
}
inline ll query(int x) // O(1)
{
ll ans = a[x];
if(laz1[id[x]]) ans += laz1[id[x]] - tim1[id[x]] * (R[id[x]] - x);
if(laz2[id[x]]) ans += laz2[id[x]] - tim2[id[x]] * (x - L[id[x]]);
ans -= laz[id[x]];
return ans;
}
signed main()
{
read(n);
len = sqrt(n);
top = n / len + (n % len > 0);
L[1] = 1;
R[top] = n;
for(int i = 1,now = 1,tim = 0;i <= n;i ++)
{
read(a[i]);
tim ++;
id[i] = now;
if(tim == len)
{
R[now] = i;
L[now + 1] = i + 1;
now ++;
tim = 0;
}
}
read(m);
for(int i = 1,op,x,y,k;i <= m;i ++)
{
read(op);
if(op == 1)
{
read(x);
read(y);
read(k);
update_del(x,y,k);
}
if(op == 2)
{
read(x);
read(y);
update_add(x,y);
}
if(op == 3)
{
read(x);
cout << query(x) << '\n';
}
}
Blue_Archive;
}
</details>