AT_abc256_h [ABC256Ex] I like Query Problem 题解
线段树每个点上维护区间最大值、最小值,
区间除以 $x$ 时,由区间对应的每个节点向下递归到 $\max=\min$ 的节点,在这些节点上区间推平。
每个节点被除 $O(\log V)$ 次后 $\max=\min$,所以总复杂度 $O(n\log n\log V)$。
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
int n, m;
struct T
{
int s, x, y, z;
} R[500050 << 2];
void u(int p)
{
R[p].s = R[p << 1].s + R[p << 1 | 1].s;
R[p].x = max(R[p << 1].x, R[p << 1 | 1].x);
R[p].y = min(R[p << 1].y, R[p << 1 | 1].y);
}
void f(int p, int s, int t, int x)
{
R[p].s = (t - s + 1) * x;
R[p].x = R[p].y = R[p].z = x;
}
void d(int p, int s, int t)
{
if (~R[p].z)
{
int m = s + t >> 1;
f(p << 1, s, m, R[p].z);
f(p << 1 | 1, m + 1, t, R[p].z);
R[p].z = -1;
}
}
void B(int s, int t, int p)
{
R[p].z = -1;
if (s == t)
return (void)(scanf("%lld", &R[p].s), R[p].x = R[p].y = R[p].s);
int m = s + t >> 1;
B(s, m, p << 1);
B(m + 1, t, p << 1 | 1);
u(p);
}
void M1(int l, int r, int x, int s, int t, int p)
{
if (l <= s && t <= r && R[p].x == R[p].y)
return f(p, s, t, R[p].x / x);
d(p, s, t);
int m = s + t >> 1;
if (l <= m)
M1(l, r, x, s, m, p << 1);
if (r > m)
M1(l, r, x, m + 1, t, p << 1 | 1);
u(p);
}
void M2(int l, int r, int x, int s, int t, int p)
{
if (l <= s && t <= r)
return f(p, s, t, x);
d(p, s, t);
int m = s + t >> 1;
if (l <= m)
M2(l, r, x, s, m, p << 1);
if (r > m)
M2(l, r, x, m + 1, t, p << 1 | 1);
u(p);
}
int Q(int l, int r, int s, int t, int p)
{
if (l <= s && t <= r)
return R[p].s;
d(p, s, t);
int m = s + t >> 1, q = 0;
if (l <= m)
q += Q(l, r, s, m, p << 1);
if (r > m)
q += Q(l, r, m + 1, t, p << 1 | 1);
return q;
}
signed main()
{
scanf("%lld%lld", &n, &m);
B(1, n, 1);
for (int i = 0, o, x, y, k; i < m; ++i)
{
scanf("%lld%lld%lld", &o, &x, &y);
switch (o)
{
case 1:
scanf("%lld", &k);
M1(x, y, k, 1, n, 1);
break;
case 2:
scanf("%lld", &k);
M2(x, y, k, 1, n, 1);
break;
case 3:
printf("%lld\n", Q(x, y, 1, n, 1));
break;
}
}
return 0;
}

浙公网安备 33010602011771号