势能线段树专题
P4145 上帝造题的七分钟 2 / 花神游历各国
代码
每个数字最多进行6次操作会变成1
假如区间内的数字都是1,即可停止操作
const int N = 100010;
int a[N];
struct T {
int l, r, val, tag;
} tr[N << 2];
void pushup(int p) {
tr[p].val = tr[p << 1].val + tr[p << 1 | 1].val;
tr[p].tag = tr[p << 1].tag + tr[p << 1 | 1].tag;
}
void build(int p, int l, int r) {
if (l == r) {
tr[p] = {l, r, a[l], 0};
return;
}
tr[p] = {l, r, 0, 0};
int mid = l + r >> 1;
build(p << 1, l, mid);
build(p << 1 | 1, mid + 1, r);
pushup(p);
}
int query(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) return tr[p].val;
int mid = tr[p].l + tr[p].r >> 1;
if (l > mid) return query(p << 1 | 1, l, r);
else if (r <= mid) return query(p << 1, l, r);
return query(p << 1, l, r) + query(p << 1 | 1, l, r);
}
void modify(int p, int l, int r) {
if (tr[p].tag == tr[p].r - tr[p].l + 1) {
return;
}
else {
if (tr[p].l == tr[p].r) {
if (tr[p].val <= 1) {
tr[p].tag = 1;
} else {
tr[p].tag = 0;
}
tr[p].val = sqrt(tr[p].val);
if (tr[p].val <= 1) tr[p].tag = 1;
else tr[p].tag = 0;
return;
} else {
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify(p<<1, l, r);
if (r > mid) modify(p<<1|1, l, r);
pushup(p);
}
}
}
void solve(int Case) {
int n, m;
cin >> n ;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
cin >> m;
for (; m--;) {
int k, l, r;
cin >> k >> l >> r;
if (!k) modify(1, min(l, r), max(l, r));
else cout << query(1, min(l, r), max(l, r)) << nline;
}
}
势能线段树二
代码
任然是每个数字最多开6次,但是区间加和之后会影响到原来的数字,假如区间的数字都相同,只需要把和变成sqrt(sun/len),这样就可以使用懒标记
const int N = 500010;
#define ls(x) x<<1
#define rs(x) x<<1|1
struct tree {
int l, r;
int add, sum, mmax, mmin;
};
int a[N];
struct Segment_Tree {
tree tr[N << 2];
void pushup(tree &p, tree &l, tree &r) {
p.sum = l.sum + r.sum;
p.mmax = max(l.mmax, r.mmax);
p.mmin = min(l.mmin, r.mmin);
}
void cal_tag(int p, int add) {
tr[p].sum += (tr[p].r - tr[p].l + 1) * add;
tr[p].mmin += add;
tr[p].mmax += add;
tr[p].add += add;
}
void pushdown(int u) {
if (tr[u].add) {
cal_tag(u << 1, tr[u].add);
cal_tag(u << 1|1, tr[u].add);
tr[u].add = 0;
}
}
void pushup(int p) {
pushup(tr[p], tr[ls(p)], tr[rs(p)]);
}
void build(int p, int l, int r) {
if (l == r) tr[p] = {l, r, 0, a[l], a[l], a[l]};
else {
tr[p] = {l, r, 0, 0, 0, 0};
int mid = l + r >> 1;
build(ls(p), l, mid), build(rs(p), mid + 1, r);
pushup(p);
}
}
void modify(int p, int l, int r, int d) {
if (tr[p].l >= l and tr[p].r <= r) {
cal_tag(p, d);
}
else {//分裂
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify(ls(p), l, r, d);
if (r > mid) modify(rs(p), l, r, d);
pushup(p);
}
}
tree query(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) return tr[p];
else {
pushdown(p);//分裂
int mid = tr[p].l + tr[p].r >> 1;
if (r <= mid) return query(ls(p), l, r);
if (l > mid) return query(rs(p), l, r);
else {
auto left = query(ls(p), l, r);
auto right = query(rs(p), l, r);
tree ret;
pushup(ret, left, right);
return ret;
}
}
}
void change(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) {
if (tr[p].mmax == tr[p].mmin) {
int val = tr[p].mmax;
val = sqrt(val);
tr[p].sum = val * (tr[p].r - tr[p].l + 1);
tr[p].add -= (tr[p].mmax - val);
tr[p].mmax = tr[p].mmin = val;
return ;
}
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) change(p << 1, l, r);
if (r > mid) change(p << 1 | 1, l, r);
pushup(p);
}
};
Segment_Tree ST;
void solve(int Case) {
int n, m;
scanf("%lld%lld", &n, &m );
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
ST.build(1, 1, n);
for (int i = 1; i <= m; i++) {
int opt, l, r, x;
scanf("%lld%lld%lld", &opt, &l, &r);
if (opt == 1) {
ST.change(1, l, r);
} else if (opt == 2) {
scanf("%lld", &x);
ST.modify(1, l, r, x);
} else {
printf("%lld\n", ST.query(1, l, r).sum);
}
}
}
D. The Child and Sequence
区间取模,区间最大值小于mod不需要操作
代码
#define ls(x) x<<1
#define rs(x) x<<1 |1
const int N = 500010;
int a[N];
struct tree {
int l, r;
int sum, mmax;
};
struct Segment_Tree {
tree tr[N << 2];
int pos[N];
void pushup(tree &p, tree &l, tree &r) {
p.sum = l.sum + r.sum;
p.mmax = max(l.mmax, r.mmax);
}
void pushup(int p) {
pushup(tr[p], tr[ls(p)], tr[rs(p)]);
}
void build(int p, int l, int r) {
if (l == r) {
tr[p] = {l, r, a[l], a[l]};
pos[l] = p;
}
else {
tr[p] = {l, r, 0, 0};
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
pushup(p);
}
}
void modify1(int p, int x, int y) {
p = pos[x];
tr[p] = {x, x, y, y};
for (; p >>= 1;) pushup(p);
}
tree query(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) return tr[p];
int mid = tr[p].l + tr[p].r >> 1;
if (r <= mid) return query(ls(p), l, r);
else if (l > mid) return query(rs(p), l, r);
else {
tree ret;
auto left = query(ls(p), l, r);
auto right = query(rs(p), l, r);
pushup(ret, left, right);
return ret;
}
}
void change(int p, int l, int r, int mod) {
if (tr[p].mmax < mod) return;
if (tr[p].l >= l and tr[p].r <= r) {
if (tr[p].l == tr[p].r and tr[p].mmax >= mod) {
tr[p].sum %= mod;
tr[p].mmax %= mod;
return;
}
}
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) change(p << 1, l, r, mod);
if (r > mid) change(p << 1 | 1, l, r, mod);
pushup(p);
}
};
Segment_Tree ST;
void solve(int Case) {
int n, m;
scanf("%lld%lld", &n, &m );
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
ST.build(1, 1, n);
//printf("%lld\n", ST.query(1, 1, n).sum);
for (int i = 1; i <= m; i++) {
int opt, l, r, x;
scanf("%lld%lld%lld", &opt, &l, &r);
if (opt == 1) {
printf("%lld\n", ST.query(1, l, r).sum);
} else if (opt == 2) {
scanf("%lld", &x);
ST.change(1, l, r, x);
} else {
ST.modify1(1, l, r);
}
}
}
F. SUM and REPLACE
代码
当区间中的最大值小于等于2时就不需要操作
#define ls(x) x<<1
#define rs(x) x<<1 |1
const int N = 500010, M = N * 2;
int f[M];
void init() {
for (int i = 1; i < M; i++) {
for (int j = i; j < M; j += i) {
f[j]++;
}
}
}
int a[N];
struct tree {
int l, r;
int sum, mmax;
};
struct Segment_Tree {
tree tr[N << 2];
int pos[N];
void pushup(tree &p, tree &l, tree &r) {
p.sum = l.sum + r.sum;
p.mmax = max(l.mmax, r.mmax);
}
void pushup(int p) {
pushup(tr[p], tr[ls(p)], tr[rs(p)]);
}
void build(int p, int l, int r) {
if (l == r) {
tr[p] = {l, r, a[l], a[l]};
pos[l] = p;
}
else {
tr[p] = {l, r, 0, 0};
int mid = l + r >> 1;
build(ls(p), l, mid);
build(rs(p), mid + 1, r);
pushup(p);
}
}
void modify1(int p, int x, int y) {
p = pos[x];
tr[p] = {x, x, y, y};
for (; p >>= 1;) pushup(p);
}
tree query(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) return tr[p];
int mid = tr[p].l + tr[p].r >> 1;
if (r <= mid) return query(ls(p), l, r);
else if (l > mid) return query(rs(p), l, r);
else {
tree ret;
auto left = query(ls(p), l, r);
auto right = query(rs(p), l, r);
pushup(ret, left, right);
return ret;
}
}
void change(int p, int l, int r) {
if (tr[p].mmax <= 2) return;
if (tr[p].l >= l and tr[p].r <= r) {
if (tr[p].mmax < 2) return;
if (tr[p].l == tr[p].r and tr[p].mmax > 2) {
tr[p].sum = f[tr[p].sum];
tr[p].mmax = f[tr[p].mmax];
return;
}
}
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) change(p << 1, l, r);
if (r > mid) change(p << 1 | 1, l, r);
pushup(p);
}
};
Segment_Tree ST;
void solve(int Case) {
int n, m;
scanf("%lld%lld", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
ST.build(1, 1, n);
for (int i = 1; i <= m; i++) {
int opt, a, b;
scanf("%lld%lld%lld", &opt, &a, &b);
if (opt == 1) {
ST.change(1, a, b);
} else {
printf("%lld\n", ST.query(1, a, b).sum);
}
}
}
D. Lowbit
代码:
每个数字加上自己的lowbit,当区间中每个数字都是2的整数次幂,就是区间乘2,可以使用懒标记
#define ls(x) x<<1
#define rs(x) x<<1|1
struct tree {
int l, r;
int sum, tag, mul;
};
int a[N];
#define lowbit(x) x&(-x)
bool check(int x) {
return (x > 0 and (x & (x - 1)) == 0);
}
struct Segment_Tree {
tree tr[N << 2];
void pushup(tree &p, tree &l, tree &r) {
p.sum = (l.sum + r.sum) % mod;
p.tag = l.tag & r.tag;
}
void cal_tag(int p, int mul) {
tr[p].sum = tr[p].sum % mod * mul % mod;
tr[p].mul = tr[p].mul % mod * mul % mod;
}
void pushdown(int p) {
if (tr[p].mul != 1) {
cal_tag(p << 1, tr[p].mul);
cal_tag(p << 1 | 1, tr[p].mul);
tr[p].mul = 1;
}
}
void pushup(int p) {
pushup(tr[p], tr[ls(p)], tr[rs(p)]);
}
void build(int p, int l, int r) {
if (l == r) tr[p] = {l, r, a[l], 0, check(a[l])};
else {
tr[p] = {l, r, 0, 0, 1};
int mid = l + r >> 1;
build(ls(p), l, mid), build(rs(p), mid + 1, r);
pushup(p);
}
}
void modify(int p, int l, int r, int d) {
if (tr[p].l >= l and tr[p].r <= r) {
cal_tag(p, d);
}
else {//分裂
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify(ls(p), l, r, d);
if (r > mid) modify(rs(p), l, r, d);
pushup(p);
}
}
tree query(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) return tr[p];
else {
pushdown(p);//分裂
int mid = tr[p].l + tr[p].r >> 1;
if (r <= mid) return query(ls(p), l, r);
if (l > mid) return query(rs(p), l, r);
else {
auto left = query(ls(p), l, r);
auto right = query(rs(p), l, r);
tree ret;
pushup(ret, left, right);
return ret;
}
}
}
void change(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) {
if (tr[p].tag) {
modify(p, tr[p].l, tr[p].r, 2);
return;
}
if (tr[p].l == tr[p].r) {
int val = tr[p].sum;
val = val + (lowbit(val));
tr[p].tag = check(val);
tr[p].sum = val;
return;
}
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) change(p << 1, l, r);
if (r > mid) change(p << 1 | 1, l, r);
pushup(p);
}
};
Segment_Tree ST;
void solve(int Case) {
int n, m;
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
ST.build(1, 1, n);
scanf("%lld", &m);
for (int i = 1; i <= m; i++) {
int opt, l, r;
scanf("%lld%lld%lld", &opt, &l, &r);
if (opt == 1) {
ST.change(1, l, r);
} else {
printf("%lld\n", ST.query(1, l, r).sum % mod);
}
}
}
Counting Stars
区间加自己的lowbit,或者自己的highbit
代码:
把一个数字的最高位和其他位分开讨论,每次加highbit相当于给最高位*2,每次减去lowbit,相当于减去一个1,
维护每个数字1的个数,当区间最大的1的个数为0时不需要操作,
const int N = 500010, mod = 998244353;
#define ls(x) x<<1
#define rs(x) x<<1|1
struct tree {
int l, r;
int sum, hb, lb;
int mul;
};
int a[N];
#define lowbit(x) x&(-x)
bool check(int x) {
return (x > 0 and (x & (x - 1)) == 0);
}
int highbit(int x) {
int cnt = 0;
while (x) cnt++, x >>= 1;
return 1LL << (cnt - 1);
}
struct Segment_Tree {
tree tr[N << 2];
void pushup(tree &p, tree &l, tree &r) {
p.sum = max(l.sum, r.sum);
p.lb = (l.lb + r.lb) % mod;
p.hb = (l.hb + r.hb) % mod;
}
void cal_tag(int p, int mul) {
tr[p].hb = tr[p].hb % mod * mul % mod;
tr[p].mul = tr[p].mul % mod * mul % mod;
}
void pushdown(int p) {
if (tr[p].mul != 1) {
cal_tag(p << 1, tr[p].mul);
cal_tag(p << 1 | 1, tr[p].mul);
tr[p].mul = 1;
}
}
void pushup(int p) {
pushup(tr[p], tr[ls(p)], tr[rs(p)]);
}
void build(int p, int l, int r) {
if (l == r) tr[p] = {l, r, __builtin_popcount(a[l]), highbit(a[l]), a[l] - highbit(a[l]), 1};
else {
tr[p] = {l, r, 0, 0, 0, 1};
int mid = l + r >> 1;
build(ls(p), l, mid), build(rs(p), mid + 1, r);
pushup(p);
}
}
void modify(int p, int l, int r, int d) {
if (tr[p].l >= l and tr[p].r <= r) {
cal_tag(p, d);
}
else {//分裂
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) modify(ls(p), l, r, d);
if (r > mid) modify(rs(p), l, r, d);
pushup(p);
}
}
tree query(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) return tr[p];
else {
pushdown(p);//分裂
int mid = tr[p].l + tr[p].r >> 1;
if (r <= mid) return query(ls(p), l, r);
if (l > mid) return query(rs(p), l, r);
else {
auto left = query(ls(p), l, r);
auto right = query(rs(p), l, r);
tree ret;
pushup(ret, left, right);
return ret;
}
}
}
void change1(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) {
if (!tr[p].sum) return;
modify(p, tr[p].l, tr[p].r, 2);
return;
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) change1(p << 1, l, r);
if (r > mid) change1(p << 1 | 1, l, r);
pushup(p);
}
void change2(int p, int l, int r) {
if (tr[p].l >= l and tr[p].r <= r) {
if (!tr[p].sum) return;
if (tr[p].l == tr[p].r) {
int val = tr[p].lb;
val -= lowbit(val);
tr[p].sum--;
tr[p].lb = val;
if (!tr[p].sum) tr[p].hb = 0;
return;
}
}
pushdown(p);
int mid = tr[p].l + tr[p].r >> 1;
if (l <= mid) change2(p << 1, l, r);
if (r > mid) change2(p << 1 | 1, l, r);
pushup(p);
}
};
Segment_Tree ST;
void solve(int Case) {
int n, m;
scanf("%lld", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
}
ST.build(1, 1, n);
scanf("%lld", &m);
for (int i = 1; i <= m; i++) {
int opt, l, r;
scanf("%lld%lld%lld", &opt, &l, &r);
if (opt == 2) {
ST.change2(1, l, r);
} else if (opt == 3) {
ST.change1(1, l, r);
} else {
tree res = ST.query(1, l, r);
printf("%lld\n", (res.hb % mod + res.lb % mod) % mod);
}
}
}