T1
题目链接
LOJ#10128. 「一本通 4.3 练习 2」花神游历各国
解题思路
首先,由于是区间问题,我们很容易想到使用线段树来维护区间和,完成区间修改操作。
但是区间修改操作并不好处理。如果对于区间里每个数都暴力地开平方,这一次操作的时间复杂度可是会退化成为
O
(
n
)
O(n)
O(n)的!
怎么办呢?我们难以想到好的方法。但是我们发现,处理数据的方式是开平方!
我们联想到指数增长的恐怖,我们就能够发现,对于每一个数,它开几次平方后就会变成0 或 1.我们可以从这个方面来进行剪枝。
详细代码
#define USEFASTERREAD 1
#define rg register
#define inl inline
#define DEBUG printf("qwq\n")
#define DEBUGd(x) printf("var %s is %lld", #x, ll(x))
#define DEBUGf(x) printf("var %s is %llf", #x, double(x))
#define putln putchar('\n')
#define putsp putchar(' ')
#define Rep(a, s, t) for(rg int a = s; a <= t; a++)
#define Repdown(a, t, s) for(rg int a = t; a >= s; a--)
typedef long long ll;
typedef unsigned long long ull;
#include<cstdio>
#if USEFASTERREAD
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
#endif
namespace IO {
inl void RS() {freopen("test.in", "r", stdin), freopen("test.out", "w", stdout);}
inl ll read() {
ll x = 0, f = 1; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
return x * f;
}
inl void write(ll x) {
if(x < 0) {putchar('-'); x = -x;}
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
inl void writeln(ll x) {write(x), putln;}
inl void writesp(ll x) {write(x), putsp;}
}
using namespace IO;
template<typename T> inline T Max(const T& x, const T& y) {return y < x ? x : y;}
template<typename T> inline T Min(const T& x, const T& y) {return y < x ? y : x;}
template<typename T> inline void Swap(T& x, T& y) {T tmp = x; x = y; y = tmp;}
template<typename T> inline T Abs(const T& x) {return x < 0 ? -x : x;}
#include<cmath>
#include<cstring>
#define Clear(arr) memset(arr, 0x00, sizeof arr)
const int MAXN = 1e5 + 5;
const int MAXM = 2e5 + 5;
int N, M;
struct Segment_tree {
#define ls o << 1
#define rs o << 1 | 1
int l[MAXN << 2], r[MAXN << 2];
ll v[MAXN << 2];
bool flag[MAXN << 2];
//flag 若是1, 表示该子树不用开根了(是0或1)
void pushup(int o) {
v[o] = v[ls] + v[rs];
flag[o] = flag[ls] && flag[rs];
}
void build(int o, int L, int R, int a[]) {
l[o] = L, r[o] = R;
if(L == R) {
v[o] = a[L];
if(v[o] == 0 || v[o] == 1) flag[o] = 1;
return;
}
int m = (L + R) >> 1;
build(ls, L, m, a);
build(rs, m + 1, R, a);
pushup(o);
}
void modify(int o, int x, int y) {
if(flag[o]) return;
if(l[o] == r[o]) {
v[o] = ll(sqrt(v[o]));
if(v[o] == 1) flag[o] = 1;
return;
}
int m = (l[o] + r[o]) >> 1;
if(x <= m) modify(ls, x, y);
if(y > m) modify(rs, x, y);
pushup(o);
}
ll sum(int o, int x, int y) {
if(x <= l[o] && r[o] <= y) return v[o];
int m = (l[o] + r[o]) >> 1;
ll ans = 0;
if(x <= m) ans += sum(ls, x, y);
if(y > m) ans += sum(rs, x, y);
return ans;
}
Segment_tree() {
Clear(l);
Clear(r);
Clear(v);
Clear(flag);
}
#undef ls
#undef rs
}tr;
int delta[MAXN];
int main() {
//RS();
N = read();
for(rg int i = 1; i <= N; i++) delta[i] = read();
tr.build(1, 1, N, delta);
M = read();
while(M--) {
int opt = read();
int l = read(), r = read();
if(opt == 1) {
writeln(tr.sum(1, l, r));
}
else tr.modify(1, l, r);
}
return 0;
}
T2
题目链接
解题思路
这道题很明显是维护区间问题,可以用线段树来做。
但是,这道题的编程难点在于
l
a
z
y
−
t
a
g
lazy-tag
lazy−tag的设置。我们为了控制时间复杂度,一定要设置两个
l
a
z
y
−
t
a
g
lazy-tag
lazy−tag,一个管乘,一个管加。那么它们的优先级很值得考虑。
多手玩,多分析,就不会出错。
对于有多个lazy-tag的题目,无后效性考虑每个lazy-tag,并计算其对之后要考虑的lazy-tag的影响(DP思想)
tip:乘法的lazy-tag一开始一定要设为1!!!
详细代码
#define USEFASTERREAD 1
#define rg register
#define inl inline
#define DEBUG printf("qwq\n")
#define DEBUGd(x) printf("var %s is %lld", #x, ll(x))
#define DEBUGf(x) printf("var %s is %llf", #x, double(x))
#define putln putchar('\n')
#define putsp putchar(' ')
#define Rep(a, s, t) for(rg int a = s; a <= t; a++)
#define Repdown(a, t, s) for(rg int a = t; a >= s; a--)
typedef long long ll;
typedef unsigned long long ull;
#include<cstdio>
#if USEFASTERREAD
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
#endif
namespace IO {
inl void RS() {freopen("test.in", "r", stdin), freopen("test.out", "w", stdout);}
inl ll read() {
ll x = 0, f = 1; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
return x * f;
}
inl void write(ll x) {
if(x < 0) {putchar('-'); x = -x;}
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
inl void writeln(ll x) {write(x), putln;}
inl void writesp(ll x) {write(x), putsp;}
}
using namespace IO;
template<typename T> inline T Max(const T& x, const T& y) {return y < x ? x : y;}
template<typename T> inline T Min(const T& x, const T& y) {return y < x ? y : x;}
template<typename T> inline void Swap(T& x, T& y) {T tmp = x; x = y; y = tmp;}
template<typename T> inline T Abs(const T& x) {return x < 0 ? -x : x;}
#include <cstring>
#define Clear(arr) memset(arr, 0x00, sizeof arr)
const int MAXN = 1e5 + 5;
int n; ll P;
struct Segment_tree {
#define ls o << 1
#define rs o << 1 | 1
int l[MAXN << 2], r[MAXN << 2];
ll v[MAXN << 2], la[MAXN << 2], lm[MAXN << 2];
void pushup(int o) {
v[o] = (v[ls] + v[rs]) % P;
}
int len(int o) {return r[o] - l[o] + 1;}
void pushdown(int o) {
if(lm[o] != 1) {
v[ls] = (v[ls] * lm[o]) % P;
v[rs] = (v[rs] * lm[o]) % P;
la[ls] = (la[ls] * lm[o]) % P;
la[rs] = (la[rs] * lm[o]) % P;
lm[ls] = (lm[ls] * lm[o]) % P;
lm[rs] = (lm[rs] * lm[o]) % P;
lm[o] = 1;
}
if(la[o]) {
v[ls] = (v[ls] + la[o] * len(ls)) % P;
v[rs] = (v[rs] + la[o] * len(rs)) % P;
la[ls] = (la[ls] + la[o]) % P;
la[rs] = (la[rs] + la[o]) % P;
la[o] = 0;
}
}
void build(int o, int L, int R, int a[]) {
l[o] = L, r[o] = R; lm[o] = 1;
if(L == R) {
v[o] = a[L];
return;
}
int m = (L + R) >> 1;
build(ls, L, m, a);
build(rs, m + 1, R, a);
pushup(o);
}
void add(int o, int x, int y, ll k) {
if(x <= l[o] && r[o] <= y) {
v[o] = (v[o] + (k * len(o)) % P) % P;
la[o] = (la[o] + k) % P;
return;
}
pushdown(o);
int m = (l[o] + r[o]) >> 1;
if(x <= m) add(ls, x, y, k);
if(y > m) add(rs, x, y, k);
pushup(o);
}
void mul(int o, int x, int y, ll k) {
if(x <= l[o] && r[o] <= y) {
v[o] = (v[o] * k) % P;
la[o] = (la[o] * k) % P;
lm[o] = (lm[o] * k) % P;
return;
}
pushdown(o);
int m = (l[o] + r[o]) >> 1;
if(x <= m) mul(ls, x, y, k);
if(y > m) mul(rs, x, y, k);
pushup(o);
}
ll sum(int o, int x, int y) {
if(x <= l[o] && r[o] <= y) return v[o];
pushdown(o);
int m = (l[o] + r[o]) >> 1;
ll ans = 0;
if(x <= m) ans = (ans + sum(ls, x, y)) % P;
if(y > m) ans = (ans + sum(rs, x, y)) % P;
return ans;
}
Segment_tree() {
Clear(l);
Clear(r);
Clear(v);
Clear(la);
Clear(lm);
}
#undef ls
#undef rs
}tr;
int a[MAXN];
int M;
int main() {
//RS();
n = read(); P = read();
Rep(i, 1, n) a[i] = read();
M = read();
tr.build(1, 1, n, a);
while(M--) {
int opt = read();
int l = read(), r = read();
if(opt == 1) {
ll c = read();
tr.mul(1, l, r, c);
} else if(opt == 2) {
ll c = read();
tr.add(1, l, r, c);
} else writeln(tr.sum(1, l, r));
}
return 0;
}
T3
题目链接
解题思路
我们一定要寻找一种高效的排序方式,使每次操作在
l
o
g
log
log级别。
于是我们就想到,我们只要维护区间内每个字符出现的次数,就可以根据这26个字母的顺序将这些次数重排。用线段树维护,每次操作时间复杂度为
O
(
26
∗
l
o
g
n
)
O(26*logn)
O(26∗logn)
详细代码
#define USEFASTERREAD 1
#define rg register
#define inl inline
#define DEBUG printf("qwq\n")
#define DEBUGd(x) printf("var %s is %lld", #x, ll(x))
#define DEBUGf(x) printf("var %s is %llf", #x, double(x))
#define putln putchar('\n')
#define putsp putchar(' ')
#define Rep(a, s, t) for(rg int a = s; a <= t; a++)
#define Repdown(a, t, s) for(rg int a = t; a >= s; a--)
typedef long long ll;
typedef unsigned long long ull;
#include<cstdio>
#if USEFASTERREAD
char In[1 << 20], *ss = In, *tt = In;
#define getchar() (ss == tt && (tt = (ss = In) + fread(In, 1, 1 << 20, stdin), ss == tt) ? EOF : *ss++)
#endif
namespace IO {
inl void RS() {freopen("test.in", "r", stdin), freopen("test.out", "w", stdout);}
inl ll read() {
ll x = 0, f = 1; char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f = -1;
for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + int(ch - '0');
return x * f;
}
inl char readc() {
char ch = getchar();
while(ch < 'a' || ch > 'z') ch = getchar();
return ch;
}
inl void write(ll x) {
if(x < 0) {putchar('-'); x = -x;}
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
inl void writeln(ll x) {write(x), putln;}
inl void writesp(ll x) {write(x), putsp;}
}
using namespace IO;
template<typename T> inline T Max(const T& x, const T& y) {return y < x ? x : y;}
template<typename T> inline T Min(const T& x, const T& y) {return y < x ? y : x;}
template<typename T> inline void Swap(T& x, T& y) {T tmp = x; x = y; y = tmp;}
template<typename T> inline T Abs(const T& x) {return x < 0 ? -x : x;}
#include<cstring>
#define Clear(arr) memset(arr, 0x00, sizeof arr);
const int MAXN = 100005;
int n, m;
struct Segment_tree {
#define ls o << 1
#define rs o << 1 | 1
int cnt[MAXN << 2][27];
int l[MAXN << 2];
int r[MAXN << 2];
int la[MAXN << 2];
void pushup(int o) {
Rep(i, 1, 26) cnt[o][i] = cnt[ls][i] + cnt[rs][i];
}
void build(int o, int L, int R, char s[]) {
l[o] = L, r[o] = R;
if(L == R) {
cnt[o][s[L] - 'a' + 1] = 1;
return;
}
int m = (L + R) >> 1;
build(ls, L, m, s);
build(rs, m + 1, R, s);
pushup(o);
}
int len(int o) {return r[o] - l[o] + 1;}
void pushdown(int o) {
if(la[o]) {
Rep(i, 1, 26)
cnt[ls][i] = cnt[rs][i] = 0;
cnt[ls][la[o]] = len(ls);
cnt[rs][la[o]] = len(rs);
la[ls] = la[rs] = la[o];
la[o] = 0;
}
}
void modify(int o, int x, int y, int k) {
if(x <= l[o] && r[o] <= y) {
Rep(i, 1, 26) cnt[o][i] = 0;
cnt[o][k] = len(o);
la[o] = k;
return;
}
pushdown(o);
int m = (l[o] + r[o]) >> 1;
if(x <= m) modify(ls, x, y, k);
if(y > m) modify(rs, x, y, k);
pushup(o);
}
void ask(int o, int x, int y, int t[]) {
if(x <= l[o] && r[o] <= y) {
Rep(i, 1, 26) t[i] += cnt[o][i];
return;
}
pushdown(o);
int m = (l[o] + r[o]) >> 1;
if(x <= m) ask(ls, x, y, t);
if(y > m) ask(rs, x, y, t);
}
void Write(int o) {
if(l[o] == r[o]) {
Rep(i, 1, 26)
if(cnt[o][i]) {
putchar('a' + i - 1);
break;
}
return;
}
pushdown(o);
Write(ls);
Write(rs);
}
Segment_tree() {
Clear(cnt);
Clear(l);
Clear(r);
Clear(la);
}
#undef ls
#undef rs
}tr;
char s[MAXN];
int t[MAXN];
int main() {
//RS();
n = read(), m = read();
Rep(i, 1, n) s[i] = readc();
tr.build(1, 1, n, s);
while(m--) {
int l = read(), r = read();
int opt = read();
Clear(t);
tr.ask(1, l, r, t);
int cup = l;
if(opt == 1) {
Rep(i, 1, 26)
if(t[i]) {
tr.modify(1, cup, cup + t[i] - 1, i);
cup += t[i];
}
} else {
Repdown(i, 26, 1)
if(t[i]) {
tr.modify(1, cup, cup + t[i] - 1, i);
cup += t[i];
}
}
}
tr.Write(1);
return 0;
}