注: segment_tree
均采用 1-Index
访问; segment_tree::reset(vector&)
中vector
为0-Index
242E. XOR on Segment
区间二进制改+lazy传递+二进制trick
You’ve got an array $a$, consisting of $n$ integers $a_1, a_2, …, a_n$. You are allowed to perform two operations on this array:
Calculate the sum of current array elements on the segment $[l,r]$, that is, count value $a_l + a_{l+1} + … + a_{r}$
Apply the xor operation with a given number x to each array element on the segment $[l,r]$, that is, execute $a_l = a_l \oplus x, a_{l+1} = a_{l+1} \oplus x,…,a_r = a_r \oplus x$ This operation changes exactly $r - l + 1$ array elements.
Expression $x \oplus y$ means applying bitwise xor operation to numbers x and y. The given operation exists in all modern programming languages, for example in language C++ and Java it is marked as “^”, in Pascal — as “xor”. You’ve got a list of m operations of the indicated type. Your task is to perform all given operations, for each sum query you should print the result you get.
template<typename T> struct segment_tree {
struct node {
ll l, r; // 区间[l,r]
T sum;
// lazy值
bool lazy_set; // xor项
ll length() const { return r - l + 1; }
ll mid() const { return (l + r) / 2; }
};
vector<node> tree;
private:
ll begin = 1, end = 1;
void flip(node& n) { n.sum = n.length() - n.sum, n.lazy_set ^= 1; }
void push_up(ll o) {
// 向上传递
ll lc = o * 2, rc = o * 2 + 1;
tree[o].sum = tree[lc].sum + tree[rc].sum;
}
void push_down(ll o) {
// 向下传递
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].lazy_set) {
flip(tree[lc]), flip(tree[rc]);
tree[o].lazy_set = false;
}
}
void update(ll o, ll l, ll r) {
ll lc = o * 2, rc = o * 2 + 1;
if (!tree[o].l) return;
if (tree[o].l == l && tree[o].r == r) { // 定位到所在区间 - 同下
// set
flip(tree[o]);
return;
}
push_down(o);
ll mid = tree[o].mid();
if (r <= mid) update(lc, l, r);
else if (mid < l) update(rc, l, r);
else {
update(lc, l, mid);
update(rc, mid + 1, r);
}
push_up(o);
}
node query(ll o, ll l, ll r) {
ll lc = o * 2, rc = o * 2 + 1;
if (!tree[o].l) return {};
if (tree[o].l == l && tree[o].r == r) return tree[o];
push_down(o);
ll mid = tree[o].mid();
if (r <= mid) return query(lc, l, r);
else if (mid < l) return query(rc, l, r);
else {
node p = query(lc, l, mid);
node q = query(rc, mid + 1, r);
return {
l, r,
p.sum + q.sum
};
}
}
void build(ll o, ll l, ll r, const T* src = nullptr, ll depth = 1) {
ll lc = o * 2, rc = o * 2 + 1;
tree[o] = {};
tree[o].l = l, tree[o].r = r;
if (l == r) {
if (src) tree[o].sum = src[l];
return;
}
ll mid = (l + r) / 2;
build(lc, l, mid, src, depth + 1);
build(rc, mid + 1, r, src, depth + 1);
push_up(o);
}
void build(const T* src = nullptr) { build(begin, begin, end, src); }
public:
void range_set(ll l, ll r) { update(begin, l, r); }
node range_query(ll l, ll r) { return query(begin, l, r); }
void reserve(const ll n) { tree.reserve(n); }
void reset(const ll n) { end = n; tree.resize(end << 2); build(); }
void reset(const vector<T>& src) {
end = src.size(); tree.resize(end << 2);
build(src.data() - 1);
}
explicit segment_tree() {};
explicit segment_tree(const ll n) : begin(1) { reset(n); }
void debug() {
ll d = 1;
for (auto& n : tree) {
if (n.depth == 0) continue;
if (n.depth != d) d = n.depth, cout << endl;
n.print();
}
cout << endl;
}
};
int main() {
fast_io();
/* El Psy Kongroo */
segment_tree<unsigned int> s[20];
ll n; cin >> n;
vector<unsigned int> arr(n); for (auto& x : arr) cin >> x;
vector<unsigned int> bits(n);
for (ll i = 0; i < 20; ++i) {
for (ll j = 0; j < n; j++) bits[j] = (arr[j] & (1ll << i)) != 0;
s[i].reset(bits);
}
ll m; cin >> m;
while (m--) {
ll op; cin >> op;
switch (op)
{
case 1:
{
// sum
ll l, r, ans = 0; cin >> l >> r;
for (ll i = 0; i < 20; ++i) {
ans += s[i].range_query(l, r).sum * (1ll << i);
}
cout << ans << endl;
break;
}
case 2:
{
// xor
ll l, r, x; cin >> l >> r >> x;
for (ll i = 0; i < 20; ++i) {
if (x & (1ll << i)) s[i].range_set(l, r); // mark as flip
}
break;
}
default:
break;
}
}
return 0;
}
920F. SUM and REPLACE
数论、单点改+剪枝
Let $D(x)$ be the number of positive divisors of a positive integer $x$. For example, $D(2)= 2$ (2 is divisible by 1 and 2), $D(6) = 4$ (6 is divisible by 1, 2, 3 and 6). You are given an array $a$ of $n$ integers. You have to process two types of queries:
REPLACE
$l,r$ - for every $i \in [l,r]$, replace $a_i$ with $D(a_i)$SUM
$l,r$ - calculate $\sum_{i=l}^{r}{a_i}$ Print the answer for eachSUM
query.
namespace eratosthenes_sieve_d {...}; // 见 板子整理
using namespace eratosthenes_sieve_d;
template<typename T> struct segment_tree {
struct node {
ll l, r; // 区间[l,r]
T sum_v;
T max_v;
ll length() const { return r - l + 1; }
ll mid() const { return (l + r) / 2; }
};
vector<node> tree;
private:
ll begin = 1, end = 1;
void push_up(ll o) {
// 向上传递
ll lc = o * 2, rc = o * 2 + 1;
tree[o].sum_v = tree[lc].sum_v + tree[rc].sum_v;
tree[o].max_v = max(tree[lc].max_v, tree[rc].max_v);
}
void update(ll o, ll l, ll r) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].max_v <= 2) return; // 剪掉!!
if (tree[o].length() == 1 && tree[o].l == l && tree[o].r == r) {
tree[o].sum_v = tree[o].max_v = D[tree[o].sum_v];
return;
}
ll mid = tree[o].mid();
if (r <= mid) update(lc, l, r);
else if (mid < l) update(rc, l, r);
else {
update(lc, l, mid);
update(rc, mid + 1, r);
}
push_up(o);
}
node query(ll o, ll l, ll r) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].l == l && tree[o].r == r) return tree[o];
ll mid = tree[o].mid();
if (r <= mid) return query(lc, l, r);
else if (mid < l) return query(rc, l, r);
else {
node p = query(lc, l, mid);
node q = query(rc, mid + 1, r);
return {
l, r,
p.sum_v + q.sum_v,
max(p.max_v, q.max_v),
};
}
}
void build(ll o, ll l, ll r, const T* src = nullptr) {
ll lc = o * 2, rc = o * 2 + 1;
tree[o] = {};
tree[o].l = l, tree[o].r = r;
if (l == r) {
if (src) tree[o].sum_v = tree[o].max_v = src[l];
return;
}
ll mid = tree[o].mid();
build(lc, l, mid, src);
build(rc, mid + 1, r, src);
push_up(o);
}
void build(const T* src = nullptr) { build(begin, begin, end, src); }
public:
void range_set(ll l, ll r) { update(begin, l, r); }
node range_query(ll l, ll r) { return query(begin, l, r); }
T range_sum(ll l, ll r) { return range_query(l, r).sum_v; }
T range_max(ll l, ll r) { return range_query(l, r).max_v; }
void reserve(const ll n) { tree.reserve(n); }
void reset(const ll n) { end = n; tree.resize(end << 2); build(); }
void reset(const vector<T>& src) {
end = src.size(); tree.resize(end << 2);
build(src.data() - 1);
}
explicit segment_tree() {};
explicit segment_tree(const ll n) : begin(1), end(n) { reset(n); }
};
int main() {
fast_io();
/* El Psy Kongroo */
init();
// 1 -> 1, 2 -> 2 无需修改
// 2 以上的区间趋近D[n]可以非常快 - log n次内可以暴力解决
ll n, m; cin >> n >> m; vec arr(n);
for (ll& x : arr) cin >> x;
segment_tree<ll> st; st.reset(arr);
while (m--) {
ll op; cin >> op;
switch (op)
{
case 1: {
// REPLACE
ll l, r; cin >> l >> r;
ll mx = st.range_max(l, r);
if (mx > 2)
st.range_set(l, r);
break;
}
case 2: {
// SUM
ll l, r; cin >> l >> r;
cout << st.range_sum(l, r) << endl;
break;
}
default:
break;
}
}
return 0;
}
1234D. Distinct Characters Queries
串转换bitset
求独特值数
You are given a string $s$ consisting of lowercase Latin letters and $q$ queries for this string. Recall that the substring $s[l; r]$ of the string $s$ is the string $s_l s_{l + 1} \dots s_r$. For example, the substrings of “codeforces” are “code”, “force”, “f”, “for”, but not “coder” and “top”. There are two types of queries:
- $1~ pos~ c$ ($1 \le pos \le |s|$, $c$ is lowercase Latin letter): replace $s_{pos}$ with $c$ (set $s_{pos} := c$);
- $2~ l~ r$ ($1 \le l \le r \le |s|$): calculate the number of distinct characters in the substring $s[l; r]$.
template<typename T> struct segment_tree {
struct node {
ll l, r; // 区间[l,r]
T value; // a-z 标记
// lazy值
optional<T> lazy_set;
ll length() const { return r - l + 1; }
ll mid() const { return (l + r) / 2; }
};
vector<node> tree;
private:
ll begin = 1, end = 1;
void push_up(ll o) {
// 向上传递
ll lc = o * 2, rc = o * 2 + 1;
tree[o].value = tree[lc].value | tree[rc].value;
}
void push_down(ll o) {
// 向下传递
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].lazy_set.has_value()) {
tree[lc].lazy_set = tree[rc].lazy_set = tree[o].lazy_set;
// 可差分操作
tree[lc].value = tree[rc].value = tree[o].lazy_set.value();
tree[o].lazy_set.reset();
}
}
void update(ll o, ll l, ll r, optional<T> const& set_v = {}, T const& add_v = 0) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].l == l && tree[o].r == r) { // 定位到所在区间 - 同下
if (set_v.has_value()) {
// set
tree[o].value = set_v.value();
tree[o].lazy_set = set_v;
}
return;
}
push_down(o); // 单点其实没必要...
ll mid = tree[o].mid();
if (r <= mid) update(lc, l, r, set_v, add_v);
else if (mid < l) update(rc, l, r, set_v, add_v);
else {
update(lc, l, mid, set_v, add_v);
update(rc, mid + 1, r, set_v, add_v);
}
push_up(o);
}
node query(ll o, ll l, ll r) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].l == l && tree[o].r == r) return tree[o];
push_down(o);
ll mid = tree[o].mid();
if (r <= mid) return query(lc, l, r);
else if (mid < l) return query(rc, l, r);
else {
node p = query(lc, l, mid);
node q = query(rc, mid + 1, r);
return { l, r, p.value | q.value };
}
}
void build(ll o, ll l, ll r, const T* src = nullptr) {
ll lc = o * 2, rc = o * 2 + 1;
tree[o] = {};
tree[o].l = l, tree[o].r = r;
if (l == r) {
if (src) tree[o].value = src[l];
return;
}
ll mid = (l + r) / 2;
build(lc, l, mid, src);
build(rc, mid + 1, r, src);
push_up(o);
}
void build(const T* src = nullptr) { build(begin, begin, end, src); }
public:
void range_set(ll l, ll r, T const& v) { update(begin, l, r, v, 0); }
node range_query(ll l, ll r) { return query(begin, l, r); }
/****/
void reserve(const ll n) { tree.reserve(n); }
void reset(const ll n) { end = n; tree.resize(end << 2); build(); }
// src: 0-based input array
void reset(const vector<T>& src) {
end = src.size(); tree.resize(end << 2);
build(src.data() - 1);
}
explicit segment_tree() {};
explicit segment_tree(const ll n) : begin(1) { reset(n); }
};
typedef bitset<32> bs;
bs from_char(char c) { return bs(1 << (c - 'a')); }
int main() {
fast_io();
/* El Psy Kongroo */
string s; cin >> s;
vector<bs> arr(s.size());
for (ll i = 0; i < s.size(); ++i) arr[i] = from_char(s[i]);
segment_tree<bs> st; st.reset(arr);
ll q; cin >> q;
while (q--) {
ll op; cin >> op;
if (op == 1) {
ll pos; char c; cin >> pos >> c;
st.range_set(pos, pos, from_char(c));
}
else {
ll l, r; cin >> l >> r;
auto ans = st.range_query(l, r);
auto bits = ans.value;
cout << bits.count() << '\n';
}
}
return 0;
}
P6492. STEP
给定一个长度为 $n$ 的字符序列 $a$,初始时序列中全部都是字符
L
。 有 $q$ 次修改,每次给定一个 $x$,若 $a_x$ 为L
,则将 $a_x$ 修改成R
,否则将 $a_x$ 修改成L
。 对于一个只含字符L
,R
的字符串 $s$,若其中不存在连续的L
和R
,则称 $s$ 满足要求。 每次修改后,请输出当前序列 $a$ 中最长的满足要求的连续子串的长度。
int len[4*N],L[4*N],R[4*N],S[4*N],H[4*N],ans[4*N];
// 原数组,节点长度,左端点,右端点,符合条件的前缀,符合条件的后缀,符合条件的最大长度
void work(int o,int k){//更新第o个节点
S[o]=H[o]=ans[o]=1;
L[o]=R[o]=k;
}
void maintain(int o){
int lc=o<<1,rc=o<<1|1;
if(L[rc]^R[lc]==0){
ans[o]=max(ans[lc],ans[rc]);
}
else{
ans[o]=max(H[lc]+S[rc],max(ans[lc],ans[rc]));
}
L[o]=L[lc],R[o]=R[rc];
if(S[lc]==len[lc]&&L[rc]^R[lc])S[o]=S[lc]+S[rc];
else S[o]=S[lc];
if(H[rc]==len[rc]&&L[rc]^R[lc])H[o]=H[rc]+H[lc];
else H[o]=H[rc];
}
void build(int o,int l,int r){
len[o]=r-l+1;
if(l==r){
work(o,0);
return;
}
int lc=o*2,rc=o*2+1,mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
maintain(o);
}
void change(int o,int l,int r,int x)
{
if(l==r)
{
work(o,!L[o]); //0变成1,1变成0
return;
}
int lc=o*2,rc=o*2+1,mid=l+r>>1;
if(x<=mid) change(lc,l,mid,x);
else change(rc,mid+1,r,x);
maintain(o);
}
int main(){
int n,q;
cin>>n>>q;
build(1,1,n);
while(q--){
int x;cin>>x;
change(1,1,n,x);
cout<<ans[1]<<endl;
}
return 0;
}
P11373 「CZOI-R2」天平
- 正解转 https://mos9527.com/posts/cp/gcd-problems/#p11373-czoi-r2%E5%A4%A9%E5%B9%B3,此处为Subtask 3解法
- TL;DR 区间维护$gcd$;同时将区间改操作化为单点改操作省去
push_down
- 给定数组$a$定义$gcd(a) = gcd(a_1,a_2,…a_n)$
- 由引理知$gcd(x,y) = gcd(x,y-x)$,可拓展为$gcd(a) = gcd(a_1, a_2 - a_1, …, a_n - a_{n-1})$
- 记差分数组为$b$,$\forall b_i \in b, b_i = a_i - a_{i-1}$,既有$gcd(a) = gcd(a_1, b_2,…,b_n)$
- 鉴于题目只要求区间加,即等效于差分数组单点改,维护$b$数组RMQ后
push_up
即可
template<typename T> struct segment_tree {
struct node {
ll l, r; // 区间[l,r]
T sum, gcd; // 差分和,差分gcd
ll length() const { return r - l + 1; }
ll mid() const { return (l + r) / 2; }
};
vector<node> tree;
private:
ll begin = 1, end = 1;
void push_up(ll o) {
// 向上传递
ll lc = o * 2, rc = o * 2 + 1;
tree[o].sum = tree[lc].sum + tree[rc].sum;
tree[o].gcd = gcd(tree[lc].gcd, tree[rc].gcd);
}
void update(ll o, ll l, ll r, ll v) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].l == l && tree[o].r == r && tree[o].length() == 1) { // 定位单点
tree[o].sum += v, tree[o].gcd = tree[o].sum;
return;
}
ll mid = tree[o].mid();
if (r <= mid) update(lc, l, r, v);
else if (mid < l) update(rc, l, r, v);
else {
update(lc, l, mid, v);
update(rc, mid + 1, r, v);
}
push_up(o);
}
node query(ll o, ll l, ll r) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].l == l && tree[o].r == r) return tree[o];
ll mid = tree[o].mid();
if (r <= mid) return query(lc, l, r);
else if (mid < l) return query(rc, l, r);
else {
node p = query(lc, l, mid);
node q = query(rc, mid + 1, r);
return { l, r, p.sum + q.sum, gcd(p.gcd, q.gcd) };
}
}
void build(ll o, ll l, ll r, const T* src = nullptr) {
ll lc = o * 2, rc = o * 2 + 1;
tree[o] = {};
tree[o].l = l, tree[o].r = r;
if (l == r) {
if (src) tree[o].sum = tree[o].gcd = src[l];
return;
}
ll mid = (l + r) / 2;
build(lc, l, mid, src);
build(rc, mid + 1, r, src);
push_up(o);
}
void build(const T* src = nullptr) { build(begin, begin, end, src); }
public:
void add(ll p, T const& v) { update(begin, p,p, v); }
node range_query(ll l, ll r) { return query(begin, l, r); }
/****/
void reserve(const ll n) { tree.reserve(n); }
void reset(const ll n) { end = n; tree.resize(end << 2); build(); }
// src: 0-based input array
void reset(const vector<T>& src) {
end = src.size(); tree.resize(end << 2);
build(src.data() - 1);
}
explicit segment_tree() {};
explicit segment_tree(const ll n) : begin(1) { reset(n); }
};
int main() {
fast_io();
/* El Psy Kongroo */
ll n,q; cin >> n >> q;
vec src(n); for (ll& x : src) cin >> x;
for (ll i = n - 1;i >= 1;i--) src[i] -= src[i-1];
segment_tree<ll> seg(n); seg.reset(src);
while (q--) {
char op; cin >> op;
switch (op) {
case 'D': {
ll x; cin >> x;
break;
}
case 'I': {
ll x,y; cin >> x>>y;
break;
}
case 'A': {
ll l,r,v; cin >> l >> r >> v;
seg.add(l,v);
if (r != n) seg.add(r+1,-v);
break;
}
case 'Q':
default:{
ll l,r,v; cin >> l >> r >> v;
ll a = seg.range_query(1,l).sum; // 差分和->a_l
ll b_gcd = seg.range_query(l + 1,r).gcd;
ll range_gcd = gcd(a,b_gcd);
if (v % range_gcd == 0) cout << "YES" << endl;
else cout << "NO" << endl;
break;
}
}
}
return 0;
}
Reference
- C++ 风格实现
template<typename T> struct segment_tree {
struct node {
ll l, r; // 区间[l,r]
T sum_v;
T max_v;
// lazy值
T lazy_add;
optional<T> lazy_set;
ll length() const { return r - l + 1; }
ll mid() const { return (l + r) / 2; }
};
vector<node> tree;
private:
ll begin = 1, end = 1;
void push_up(ll o) {
// 向上传递
ll lc = o * 2, rc = o * 2 + 1;
tree[o].sum_v = tree[lc].sum_v + tree[rc].sum_v;
tree[o].max_v = max_v(tree[lc].max_v, tree[rc].max_v);
}
void push_down(ll o) {
// 向下传递
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].lazy_set.has_value()) {
tree[lc].lazy_add = tree[rc].lazy_add = 0;
tree[lc].lazy_set = tree[rc].lazy_set = tree[o].lazy_set;
// 可差分操作
tree[lc].max_v = tree[o].lazy_set.value();
tree[rc].max_v = tree[o].lazy_set.value();
// 求和贡献与长度有关
tree[lc].sum_v = tree[o].lazy_set.value() * tree[lc].length();
tree[rc].sum_v = tree[o].lazy_set.value() * tree[rc].length();
tree[o].lazy_set.reset();
}
if (tree[o].lazy_add) {
tree[lc].lazy_add += tree[o].lazy_add, tree[rc].lazy_add += tree[o].lazy_add;
// 同上
tree[lc].max_v += tree[o].lazy_add;
tree[rc].max_v += tree[o].lazy_add;
tree[lc].sum_v += tree[o].lazy_add * tree[lc].length();
tree[rc].sum_v += tree[o].lazy_add * tree[rc].length();
tree[o].lazy_add = {};
}
}
void update(ll o, ll l, ll r, optional<T> const& set_v = {}, T const& add_v = 0) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].l == l && tree[o].r == r) { // 定位到所在区间 - 同下
if (set_v.has_value()) {
// set
tree[o].max_v = set_v.value();
tree[o].sum_v = set_v.value() * tree[o].length();
tree[o].lazy_set = set_v; tree[o].lazy_add = {};
}
else {
// add
tree[o].max_v += add_v;
tree[o].sum_v += add_v * tree[o].length();
tree[o].lazy_add += add_v;
}
return;
}
push_down(o);
ll mid = tree[o].mid();
if (r <= mid) update(lc, l, r, set_v, add_v);
else if (mid < l) update(rc, l, r, set_v, add_v);
else {
update(lc, l, mid, set_v, add_v);
update(rc, mid + 1, r, set_v, add_v);
}
push_up(o);
}
node query(ll o, ll l, ll r) {
ll lc = o * 2, rc = o * 2 + 1;
if (tree[o].l == l && tree[o].r == r) return tree[o];
push_down(o);
ll mid = tree[o].mid();
if (r <= mid) return query(lc, l, r);
else if (mid < l) return query(rc, l, r);
else {
node p = query(lc, l, mid);
node q = query(rc, mid + 1, r);
return {
l, r,
p.sum_v + q.sum_v,
max(p.max_v, q.max_v),
};
}
}
void build(ll o, ll l, ll r, const T* src = nullptr) {
ll lc = o * 2, rc = o * 2 + 1;
tree[o] = {};
tree[o].l = l, tree[o].r = r;
if (l == r) {
if (src) tree[o].sum_v = tree[o].max_v = src[l];
return;
}
ll mid = (l + r) / 2;
build(lc, l, mid, src);
build(rc, mid + 1, r, src);
push_up(o);
}
void build(const T* src = nullptr) { build(begin, begin, end, src); }
public:
void range_add(ll l, ll r, T const& v) { update(begin, l, r, {}, v); }
void range_set(ll l, ll r, T const& v) { update(begin, l, r, v, 0); }
node range_query(ll l, ll r) { return query(begin, l, r); }
T range_sum(ll l, ll r) { return range_query(l, r).sum_v; }
T range_max(ll l, ll r) { return range_query(l, r).max_v; }
void reserve(const ll n) { tree.reserve(n); }
void reset(const ll n) { end = n; tree.resize(end << 2); build(); }
// src -> 0开始的叶子节点
void reset(const vector<T>& src) {
end = src.size(); tree.resize(end << 2);
build(src.data() - 1); // 邪恶指针trick - 毕竟我们的访问从1开始
}
explicit segment_tree() {};
explicit segment_tree(const ll n) : begin(1), end(n) { reset(n); }
};