HDU 4893 线段树延迟标记
题意
-
初始有一长度为n,全为0的序列
-
每次我们可以对这个序列进行三种操作:
-
1.将某个位置的数加上d
-
2.输出某个区间的和
-
3.将某个区间内每个数字变为与其最接近的斐波那契数,如果有两个最相近的数,则取更小的那个。
-
思路
-
首先对于寻找最近的斐波那契数,我们可以预处理一个90+大小的数组来保存斐波那契数列的前90项,此时范围其实已经要覆盖long long了,完全够用,可以用lower_bound来查询
-
然后就是这个区间和可以用线段树保存,对于区间的3操作,肯定是不能一个一个去操作的,所以我们可以想到在操作前我们就保存好这个区间的“斐波那契和”。
-
这样的话,对于每次单点修改操作,我们都要更新一遍线段树路径上的区间和与“斐波那契和”,可以发现,题目中没有区间加和修改的操作,正是我们这种方法得以实行的原因——每次只需要更新一条路上的数据。然后我们使用lazy标记确保3操作的时间复杂度即可。
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
struct ab
{
long long v;
long long fv;
bool fl;
} aa[400005];
int n, m, co = 0;
long long fb[1005];
long long abss(long long a)
{
if (a < 0)
{
a = -a;
}
return a;
}
void build(int o, int l, int r)
{
aa[o].fl = false;
if (l == r)
{
aa[o].v = 0;
aa[o].fv = 1;
return;
}
int mid = (l + r) >> 1;
build(o << 1, l, mid);
build(o << 1 | 1, mid + 1, r);
aa[o].v = aa[o << 1].v + aa[o << 1 | 1].v;
aa[o].fv = aa[o << 1].fv + aa[o << 1 | 1].fv;
}
void pd(int o)
{
if (aa[o].fl)
{
aa[o].fl = false;
aa[o << 1].v = aa[o << 1].fv;
aa[o << 1].fl = true;
aa[o << 1 | 1].v = aa[o << 1 | 1].fv;
aa[o << 1 | 1].fl = true;
}
}
void update(int o, int l, int r, int k, long long x)
{
if (l == r)
{
aa[o].v += x;
int ll = lower_bound(fb, fb + 91, aa[o].v) - fb;
if (!ll)
{
aa[o].fv = 1;
}
else if (abss(fb[ll] - aa[o].v) < abss(fb[ll - 1] - aa[o].v))
{
aa[o].fv = fb[ll];
}
else
{
aa[o].fv = fb[ll - 1];
}
return;
}
pd(o);
int mid = (l + r) >> 1;
if (l <= k && mid >= k)
{
update(o << 1, l, mid, k, x);
}
if (mid < k && r >= k)
{
update(o << 1 | 1, mid + 1, r, k, x);
}
aa[o].v = aa[o << 1].v + aa[o << 1 | 1].v;
aa[o].fv = aa[o << 1].fv + aa[o << 1 | 1].fv;
}
void change(int o, int l, int r, int x, int y)
{
if (x <= l && y >= r)
{
aa[o].fl = true;
aa[o].v = aa[o].fv;
return;
}
pd(o);
int mid = (l + r) >> 1;
if (x <= mid)
{
change(o << 1, l, mid, x, y);
}
if (y > mid)
{
change(o << 1 | 1, mid + 1, r, x, y);
}
aa[o].v = aa[o << 1].v + aa[o << 1 | 1].v;
aa[o].fv = aa[o << 1].fv + aa[o << 1 | 1].fv;
}
long long qu(int o, int l, int r, int x, int y)
{
if (l >= x && r <= y)
{
return aa[o].v;
}
pd(o);
int mid = (l + r) >> 1;
long long ans1 = 0, ans2 = 0;
if (x <= mid)
{
ans1 = qu(o << 1, l, mid, x, y);
}
if (y > mid)
{
ans2 = qu(o << 1 | 1, mid + 1, r, x, y);
}
return ans1 + ans2;
}
int main()
{
fb[1] = fb[0] = 1;
for (int i = 2; i <= 90; ++i)
{
fb[i] = fb[i - 2] + fb[i - 1];
}
while (scanf("%d%d", &n, &m) == 2)
{
build(1, 1, n);
for (int i = 1; i <= m; ++i)
{
int q, l, r;
scanf("%d", &q);
if (q == 1)
{
int t;
long long xx;
scanf("%d%lld", &t, &xx);
update(1, 1, n, t, xx);
}
else if (q == 2)
{
scanf("%d%d", &l, &r);
printf("%lld\n", qu(1, 1, n, l, r));
}
else
{
scanf("%d%d", &l, &r);
change(1, 1, n, l, r);
}
}
}
return 0;
}