Contest Info
[Practice Link](https://www.jisuanke.com/contest/3258?view=challenges)
Solved | A | B | C | D | E | F |
---|---|---|---|---|---|---|
6/6 | O | O | O | O | Ø | Ø |
- O 在比赛中通过
- Ø 赛后通过
- ! 尝试了但是失败了
- - 没有尝试
Solutions
A. 爱喝「肥宅快乐水」的班长
题意:
需要买\(n\)瓶饮料,一共有\(m\)种饮料,但是一定要买一瓶可乐,保证\(m\)种饮料里面一定有一瓶可乐。
问购买的方案数。
思路:
考虑将\(n\)个球放到\(m\)个盒子里面,但是有一个盒子已经有一个球了,那么解数就是\({n + m - 2 \choose m - 1}\)
代码:
view code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 2e3 + 10;
const ll p = 1e9 + 7;
int n, m;
ll fac[N], inv[N];
ll qmod(ll base, ll n) {
ll res = 1;
while (n) {
if (n & 1) res = res * base % p;
base = base * base % p;
n >>= 1;
}
return res;
}
ll C(int n, int m) {
if (m > n) return 0;
return fac[n] * inv[m] % p * inv[n - m] % p;
}
int main() {
fac[0] = 1;
for (int i = 1; i < N; ++i) fac[i] = fac[i - 1] * i % p;
inv[N - 1] = qmod(fac[N - 1], p - 2);
for (int i = N - 1; i >= 1; --i) inv[i - 1] = inv[i] * i % p;
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d", &n, &m);
ll res = C(n + m - 2, m - 1);
printf("%lld\n", res);
}
return 0;
}
B. 整数对
题意:
给出\(n, m, p\),询问\(a \times b = k \times p(1 \leq a \leq n, 1 \leq b \leq m)\)的整数对\((a, b)\)的数量。
思路:
考虑将\([1, n]\)取模后映射到\([0, p - 1]\),再枚举一个数\(x\),那么要有一个\(y\)满足\(xy = kp\)那么必然有\(lcm(x, p)\;|\;xy\),即\(\frac{x}{gcd(x, p)}\;|\;y\)
那么枚举一下即可。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long
int n, m, p;
ll a[N], b[N], f[N];
ll lcm(ll a, ll b) {
return a / __gcd(a, b) * b;
}
int main() {
int T; scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &p);
for (int i = 0; i <= p; ++i) a[i] = 0, b[i] = 0;
for (int i = 0; i < p; ++i) {
a[i] = n / p + (n % p >= i);
b[i] = m / p + (m % p >= i);
}
--a[0]; --b[0];
a[p] = a[0]; b[p] = b[0];
for (int i = 1; i <= p; ++i) {
f[i] = 0;
for (int j = i; j <= p; j += i) {
f[i] += b[j];
}
}
ll res = 0;
for (int i = 1; i <= p; ++i) {
ll LCM = lcm(i, p);
ll base = LCM / i;
res += a[i] * f[base];
}
printf("%lld\n", res);
}
return 0;
}
C. 码队的新桌游
题意:
有\(n\)个人玩游戏,每个人有三张卡片\(a_i, b_i, c_i(a_i < b_i < c_i)\),两个玩家之间进行对局,一共三小轮,每个玩家打出一张在这一局中从来没有用过的卡,谁的卡上的数字大谁就胜利。
对于一局比赛,如果双方都有可能赢下这局比赛,那么就称这局比赛的结果是存在悬念的。
问对于每个玩家,它的\(n - 1\)的个对局中有多少个对局是有悬念的。
思路:
对于玩家\(i\),它有\((a_i, b_i, c_i)\):
那么考虑哪些玩家\(j\)和它的对局是必败的。
- \(b_j < a_i\) 或者\(c_j < b_i\)
但是发现这两个条件同时成立的玩家\(j\)多算了,减去即可。
经典二维数点问题。
必胜的同理考虑。
那么剩下的就是有悬念的。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 10;
int n;
struct Hash {
int a[N], cnt;
void init() { cnt = 0; }
void add(int x) { a[++cnt] = x; }
void gao() {
sort(a + 1, a + 1 + cnt);
cnt = unique(a + 1, a + 1 + cnt) - a - 1;
}
int get(int x) {
return lower_bound(a + 1, a + 1 + cnt, x) - a;
}
}hs;
struct node {
int a, b, c;
void scan() {
scanf("%d%d%d", &a, &b, &c);
hs.add(a); hs.add(b); hs.add(c);
}
}a[N];
int res[N];
struct qnode {
//0插入 1询问
int a, b, id, op;
qnode() {}
qnode(int a, int b, int id, int op) : a(a), b(b), id(id), op(op) {}
};
struct BIT {
int a[N];
void init() {
memset(a, 0, sizeof a);
}
void update(int x, int v) {
for (; x < N; x += x & -x) {
a[x] += v;
}
}
int query(int x) {
int res = 0;
for (; x > 0; x -= x & -x) {
res += a[x];
}
return res;
}
int query(int l, int r) {
if (l > r) return 0;
return query(r) - query(l - 1);
}
}bit[3];
int main() {
while (scanf("%d", &n) != EOF) {
hs.init();
for (int i = 1; i <= n; ++i) a[i].scan();
hs.gao();
for (int i = 1; i <= n; ++i) {
a[i].a = hs.get(a[i].a);
a[i].b = hs.get(a[i].b);
a[i].c = hs.get(a[i].c);
// printf("%d %d %d %d\n", i, a[i].a, a[i].b, a[i].c);
}
for (int i = 0; i < 3; ++i) bit[i].init();
for (int i = 1; i <= n; ++i) {
bit[0].update(a[i].a, 1);
bit[1].update(a[i].b, 1);
bit[2].update(a[i].c, 1);
}
int m = hs.cnt;
for (int i = 1; i <= n; ++i) res[i] = n - 1;
for (int i = 1; i <= n; ++i) {
bit[0].update(a[i].a, -1);
bit[1].update(a[i].b, -1);
bit[2].update(a[i].c, -1);
res[i] -= bit[1].query(1, a[i].a - 1);
res[i] -= bit[2].query(1, a[i].b - 1);
res[i] -= bit[0].query(a[i].b + 1, m);
res[i] -= bit[1].query(a[i].c + 1, m);
bit[0].update(a[i].a, 1);
bit[1].update(a[i].b, 1);
bit[2].update(a[i].c, 1);
}
//必胜容斥
vector <qnode> vec; bit[0].init();
for (int i = 1; i <= n; ++i) {
vec.push_back(qnode(a[i].b, a[i].c, i, 0));
vec.push_back(qnode(a[i].a, a[i].b, i, 1));
}
sort(vec.begin(), vec.end(), [](qnode x, qnode y) {
if (x.a != y.a) return x.a < y.a;
if (x.b != y.b) return x.b > y.b;
return x.op > y.op;
});
for (auto it : vec) {
if (it.op == 0) {
bit[0].update(it.b, 1);
} else {
res[it.id] += bit[0].query(1, it.b - 1);
}
}
//必败容斥
vec.clear(); bit[0].init();
for (int i = 1; i <= n; ++i) {
vec.push_back(qnode(a[i].a, a[i].b, i, 0));
vec.push_back(qnode(a[i].b, a[i].c, i, 1));
}
sort(vec.begin(), vec.end(), [](qnode x, qnode y) {
if (x.a != y.a) return x.a > y.a;
if (x.b != y.b) return x.b < y.b;
return x.op > y.op;
});
for (auto it : vec) {
if (it.op == 0) {
bit[0].update(it.b, 1);
} else {
res[it.id] += bit[0].query(it.b + 1, m);
}
}
for (int i = 1; i <= n; ++i)
printf("%d\n", res[i]);
}
return 0;
}
D. 多项式
题意:
求树上每条路径的边权之和的\(k\)次方的和。
思路:
考虑\(f[i][j]\)表示以\(i\)为根的子树,以\(i\)为端点的所有路径的\(j\)次方和。
\(g[i][j]\)表示从\(i\)的父亲方向过来的以\(i\)为端点的所有路径的\(j\)次方和。
然后二项式转移即可。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair <int, int>
#define fi first
#define se second
const int N = 1e5 + 10;
const ll p = 998244353;
int n, K;
vector <vector<pii>> G;
ll C[20][20];
ll res;
ll qmod(ll base, ll n) {
ll res = 1;
while (n) {
if (n & 1) res = res * base % p;
base = base * base % p;
n >>= 1;
}
return res;
}
void add(ll &x, ll y) {
x += y;
if (x >= p) x -= p;
}
int fa[N]; ll f[N][15], g[N][15], h[15];
void DFS(int u) {
memset(f[u], 0, sizeof f[u]);
f[u][0] = 1;
for (auto it : G[u]) {
int v = it.fi, w = it.se;
if (v == fa[u]) continue;
fa[v] = u;
DFS(v);
h[0] = 1;
for (int i = 1; i <= K; ++i)
h[i] = h[i - 1] * w % p;
for (int i = K; i >= 0; --i) {
ll tmp = 0;
for (int j = 0; j <= i; ++j) {
add(tmp, C[i][j] * h[i - j] % p * f[v][j] % p);
}
add(f[u][i], tmp);
}
}
add(res, f[u][K]);
}
void DFS2(int u) {
for (auto it : G[u]) {
int v = it.fi, w = it.se;
if (v == fa[u]) continue;
memset(g[v], 0, sizeof g[v]);
h[0] = 1;
for (int i = 1; i <= K; ++i) {
h[i] = h[i - 1] * w % p;
}
ll hh[15]; memset(hh, 0, sizeof hh);
for (int i = K; i >= 0; --i) {
ll tmp = 0;
for (int j = 0; j <= i; ++j) {
add(tmp, C[i][j] * h[i - j] % p * f[v][j] % p);
}
add(hh[i], tmp);
}
for (int i = K; i >= 0; --i) {
ll tmp = 0;
for (int j = 0; j <= i; ++j) {
ll t = g[u][j] + f[u][j] - hh[j];
t = (t + p) % p;
add(tmp, C[i][j] * h[i - j] % p * t % p);
}
add(g[v][i], tmp);
}
add(res, g[v][K]);
DFS2(v);
}
}
int main() {
for (int i = 0; i < 20; ++i) C[i][0] = C[i][i] = 1;
for (int i = 1; i < 20; ++i)
for (int j = 1; j < i; ++j)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % p;
while (scanf("%d%d", &n, &K) != EOF) {
G.clear(); G.resize(n + 1);
for (int i = 1, u, v, w; i < n; ++i) {
scanf("%d%d%d", &u, &v, &w);
G[u].push_back(pii(v, w));
G[v].push_back(pii(u, w));
}
res = 0;
DFS(1);
// for (int i = 0; i <= K; ++i) {
// printf("%lld %lld\n", f[1][i], f[2][i]);
// }
memset(g[1], 0, sizeof g[1]);
DFS2(1);
// for (int i = 1; i <= n; ++i)
// printf("%d %lld\n", i, g[i][K]);
ll inv = qmod(n, p - 2);
res = res * inv % p * inv % p;
printf("%lld\n", res);
}
return 0;
}
E. 权值翻转
题意:
给出一棵树,每个点有点权,支持三种操作:
- 将路径上每个点的权值翻转
- 将子树中的权值翻转
- 查询一条路径上的权值的最大值
翻转操作即为将数字翻转,例如\(120\)翻转一次成为\(21\)(去掉前导\(0\)),再翻转一次成为\(12\)。
思路:
- 因为数字很大,但是只有\(50\)位,所以可以考虑用\(3\)个\(long \;long\)来存放。
- 注意到一个数字翻转的状态可能有三种,但是第一种状态只会出现一次,所以对于第一次翻转的直接暴力翻转。
- 第二次翻转以及之后的翻转打个标记。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll p = 1e6 + 7;
const ll D = 1000000000000000000ll % p;
const int N = 3e4 + 10;
int n, q;
string w[N];
struct Num {
ll a[3], v;
Num() {
a[0] = a[1] = a[2] = 0;
v = 0;
}
Num(string s) {
v = 0;
reverse(s.begin(), s.end());
while (s.size() < 54) s += "0";
reverse(s.begin(), s.end());
// cout << s << endl;
a[0] = a[1] = a[2] = 0;
for (int i = 0; i < 54; ++i) {
a[i / 18] = a[i / 18] * 10 + s[i] - '0';
//a[i / 18] %= p;
}
for (int i = 0; i < 3; ++i) {
v = v * (D % p) % p + (a[i] % p);
v %= p;
}
}
bool operator < (const Num &other) const {
for (int i = 0; i < 3; ++i) {
if (a[i] != other.a[i])
return a[i] < other.a[i];
}
return 0;
}
};
vector <vector<int>> G;
int fa[N], deep[N], sze[N], son[N], top[N], in[N], fin[N], out[N], cnt;
void DFS(int u) {
sze[u] = 1;
for (int v : G[u]) {
if (v == fa[u]) continue;
fa[v] = u;
deep[v] = deep[u] + 1;
DFS(v);
sze[u] += sze[v];
if (!son[u] || sze[v] > sze[son[u]]) {
son[u] = v;
}
}
}
void gettop(int u, int tp) {
top[u] = tp;
in[u] = ++cnt;
fin[cnt] = u;
out[u] = cnt;
if (!son[u]) {
return;
}
gettop(son[u], tp);
for (int v : G[u]) {
if (v == son[u] || v == fa[u]) continue;
gettop(v, v);
}
out[u] = cnt;
}
struct SEG {
struct node {
Num x[3];
int lazy, add;
node() {
for (int i = 0; i < 3; ++i)
x[i] = Num();
lazy = 0; add = 0;
}
void up() {
swap(x[0], x[1]);
lazy ^= 1;
}
node operator + (const node &other) const {
node res = node();
for (int i = 0; i < 3; ++i)
res.x[i] = max(x[i], other.x[i]);
res.add = add && other.add;
return res;
}
}t[N << 2], res;
void build(int id, int l, int r) {
t[id] = node();
if (l == r) {
string &s = w[fin[l]];
t[id].x[0] = Num(s);
reverse(s.begin(), s.end());
t[id].x[1] = Num(s);
reverse(s.begin(), s.end());
while (s.size() > 1 && s.back() == '0') s.pop_back();
t[id].x[2] = Num(s);
// cout << t[id].x.v << " " << t[id].y.v << " " << t[id].z.v << endl;
return;
}
int mid = (l + r) >> 1;
build(id << 1, l, mid);
build(id << 1 | 1, mid + 1, r);
t[id] = t[id << 1] + t[id << 1 | 1];
}
void pushdown(int id) {
int &lazy = t[id].lazy;
if (lazy) {
t[id << 1].up();
t[id << 1 | 1].up();
lazy = 0;
}
}
void update(int id, int l, int r, int ql, int qr) {
if (l == r) {
if (t[id].add == 0) {
t[id].add = 1;
t[id].x[0] = t[id].x[1];
t[id].x[1] = t[id].x[2];
} else {
t[id].up();
}
return;
}
if (t[id].add && l >= ql && r <= qr) {
t[id].up();
return;
}
int mid = (l + r) >> 1;
pushdown(id);
if (ql <= mid) update(id << 1, l, mid, ql, qr);
if (qr > mid) update(id << 1 | 1, mid + 1, r, ql, qr);
t[id] = t[id << 1] + t[id << 1 | 1];
}
void query(int id, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) {
res = res + t[id];
return;
}
int mid = (l + r) >> 1;
pushdown(id);
if (ql <= mid) query(id << 1, l, mid, ql, qr);
if (qr > mid) query(id << 1 | 1, mid + 1, r, ql, qr);
}
}seg;
void update(int u, int v) {
while (top[u] != top[v]) {
if (deep[top[u]] < deep[top[v]]) swap(u, v);
seg.update(1, 1, n, in[top[u]], in[u]);
u = fa[top[u]];
}
if (deep[u] > deep[v]) swap(u, v);
seg.update(1, 1, n, in[u], in[v]);
}
void query(int u, int v) {
while (top[u] != top[v]) {
if (deep[top[u]] < deep[top[v]]) {
swap(u, v);
}
seg.query(1, 1, n, in[top[u]], in[u]);
u = fa[top[u]];
}
if (deep[u] > deep[v]) swap(u, v);
seg.query(1, 1, n, in[u], in[v]);
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
while (cin >> n) {
G.clear(); G.resize(n + 1);
cnt = 0;
for (int i = 1; i <= n; ++i) son[i] = 0;
for (int i = 1; i <= n; ++i) {
cin >> w[i];
}
for (int i = 1, u, v; i < n; ++i) {
cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
DFS(1); gettop(1, 1);
seg.build(1, 1, n);
cin >> q; int op, u, v;
for (int qq = 1; qq <= q; ++qq) {
cin >> op >> u;
if (op == 1) {
cin >> v;
update(u, v);
} else if (op == 2) {
seg.update(1, 1, n, in[u], out[u]);
} else {
cin >> v;
seg.res = SEG::node();
query(u, v);
cout << seg.res.x[0].v << "\n";
}
}
}
return 0;
}
F. 码队的跑团历险
题意:
给出一个\(01\)序列,每一位都对应一个权值。
现在要支持两种操作:
- 修改某一位的权值
- 询问区间\([l, r]\),首先进行合并,两个相邻的\(0\)和\(1\)之间可以合并,直到最后没有可以合并,那么输出剩下的那些位对应的权值的最大值。询问是独立的,也就是说一次询问的合并在询问结束之后是还原回去的。
思路:
考虑一个\(01\)序列可以对应到树上的一段欧拉序,\(0\)表示入栈,\(1\)表示出栈,先把\(01\)序列补全,注意最外面套个\(01\),表示根。
然后考虑对于树上每个点维护两个权值,一个是入栈的值,一个是出栈的值。
那么再考虑\(01\)序列,感性理解一下,合并之后肯定是一段连续的\(1\)和一段连续的\(0\),因为\(10\)不可以合并。
那么一段连续的\(1\)在树上对应一段出栈序列(一条链),我们只考虑\(1\),对于\(0\)同理分析即可。
那么这条链的两个端点是什么?
我们令\(f[i]\)表示前\(i\)个数中\(1\)的个数减去\(0\)的个数,我们发现其中一个端点就是\(u\)满足\(f[u] > f[l - 1]\),那么另一个端点就是\(f[v]\)满足\(f[v]\)是\(f[l] - f[r]\)中最大的。
如果\(u > r\)那么这条链就是空链。
那么就是链上的查询和修改。
代码:
view code
#include <bits/stdc++.h>
using namespace std;
const int N = 6e5 + 10;
const int M = 25;
const int INF = 0x3f3f3f3f;
int n, m, _n, q, a[N], _a[N], b[N], pre, suf, f[N], g[N], nxf[N], preg[N], fa[N], deep[N], sze[N], son[N], top[N], in[N], fin[N], out[N], sta[N];
vector <vector<int>> G;
void dfs(int u) {
sze[u] = 1; son[u] = 0;
for (auto &v : G[u]) {
fa[v] = u;
deep[v] = deep[u] + 1;
dfs(v);
sze[u] += sze[v];
if (!son[u] || sze[v] > sze[son[u]]) son[u] = v;
}
}
void gettop(int u, int tp) {
top[u] = tp;
in[u] = ++*in;
fin[*in] = u;
if (son[u]) gettop(son[u], tp);
for (auto &v : G[u]) if (v != son[u])
gettop(v, v);
out[u] = *in;
}
void gettree() {
G.clear(); G.resize(_n + 10);
m = 0;
int rt = 0; *in = 0;
deep[0] = 0;
for (int i = 1; i <= _n; ++i) {
if (a[i] == 0) {
++m;
_a[i] = m;
fa[m] = rt;
G[rt].push_back(m);
rt = m;
} else {
_a[i] = rt;
rt = fa[rt];
}
}
fa[1] = 1; deep[1] = 0;
dfs(1);
gettop(1, 1);
}
struct SEG {
int t[N << 2];
inline void up(int id) { t[id] = max(t[id << 1], t[id << 1 | 1]);}
void build(int id, int l, int r) {
t[id] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(id << 1, l, mid);
build(id << 1 | 1, mid + 1, r);
}
void update(int id, int l, int r, int pos, int v) {
if (l == r) {
t[id] = v;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) update(id << 1, l, mid, pos, v);
else update(id << 1 | 1, mid + 1, r, pos, v);
up(id);
}
int query(int id, int l, int r, int ql, int qr) {
if (l >= ql && r <= qr) return t[id];
int mid = (l + r) >> 1;
int res = 0;
if (ql <= mid) res = max(res, query(id << 1, l, mid, ql, qr));
if (qr > mid) res = max(res, query(id << 1 | 1, mid + 1, r, ql, qr));
return res;
}
}seg[2]; // 0 in 1 out
int query(int u, int v, SEG &seg) {
int res = 0;
while (top[u] != top[v]) {
if (deep[top[u]] < deep[top[v]]) swap(u, v);
res = max(res, seg.query(1, 1, m, in[top[u]], in[u]));
u = fa[top[u]];
}
if (deep[u] > deep[v]) swap(u, v);
res = max(res, seg.query(1, 1, m, in[u], in[v]));
return res;
}
using pII = pair <int, int>;
#define fi first
#define se second
struct RMQ {
pII Max[N][M];
int mm[N];
function<pII(pII, pII)> max;
void init(int n, int *b, const function<pII(pII, pII)> &_max) {
max = _max;
mm[0] = -1;
for (int i = 1; i <= n; ++i) {
mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
Max[i][0] = pII(b[i], i);
}
for (int j = 1; j <= mm[n]; ++j) {
for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
Max[i][j] = max(Max[i][j - 1], Max[i + (1 << (j - 1))][j - 1]);
}
}
}
int queryMax(int x, int y) {
int k = mm[y - x + 1];
return max(Max[x][k], Max[y - (1 << k) + 1][k]).se;
}
}rmq_f, rmq_g;
int getin(int l, int r) {
int pr = preg[r + 1], pl = rmq_g.queryMax(l, r);
// cout << pl << " " << pr << endl;
if (pr < l) return 0;
int u = _a[pl], v = _a[pr];
return query(u, v, seg[0]);
}
int getout(int l, int r) {
int pl = nxf[l - 1], pr = rmq_f.queryMax(l, r);
// cout << pl << " " << pr << " " << l << " " << r << endl;
if (pl > r) return 0;
int u = _a[pl], v = _a[pr];
return query(u, v, seg[1]);
}
int getpos(int x, const function<bool(int, int)> &ok) {
int l = 1, r = *sta, pos = 1;
while (r - l >= 0) {
int mid = (l + r) >> 1;
if (ok(mid, x)) {
pos = mid;
l = mid + 1;
} else
r = mid - 1;
}
return pos;
}
int main() {
while (scanf("%d%d", &n, &q) != EOF) {
pre = suf = 0;
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
for (int i = 1; i <= n; ++i) scanf("%d", b + i);
f[0] = 0;
for (int i = 1; i <= n; ++i) {
f[i] = f[i - 1] + (a[i] == 1 ? 1 : -1);
pre = max(pre, f[i]);
}
g[n + 1] = 0;
for (int i = n; i >= 1; --i) {
g[i] = g[i + 1] + (a[i] == 1 ? -1 : 1);
suf = max(suf, g[i]);
}
++pre; ++suf; _n = pre + n + suf;
for (int i = n; i >= 1; --i) {
a[pre + i] = a[i];
b[pre + i] = b[i];
}
for (int i = 1; i <= pre; ++i) {
a[i] = b[i] = 0;
}
for (int i = pre + n + 1; i <= _n; ++i) {
a[i] = 1;
b[i] = 0;
}
f[0] = 0; g[_n + 1] = 0;
for (int i = 1; i <= _n; ++i) f[i] = f[i - 1] + (a[i] == 1 ? 1 : -1);
for (int i = _n; i >= 1; --i) g[i] = g[i + 1] + (a[i] == 1 ? -1 : 1);
gettree();
seg[0].build(1, 1, m); seg[1].build(1, 1, m);
for (int i = 1; i <= _n; ++i) {
seg[a[i]].update(1, 1, m, in[_a[i]], b[i]);
}
f[_n + 1] = INF; f[0] = 0;
*sta = 0;
sta[++*sta] = _n + 1;
for (int i = _n; i >= 0; --i) {
nxf[i] = sta[getpos(i, [&](int x, int y) { return f[sta[x]] > f[y]; })];
while (*sta && f[i] > f[sta[*sta]]) --*sta;
sta[++*sta] = i;
}
g[0] = INF; g[_n + 1] = 0;
*sta = 0;
sta[++*sta] = 0;
for (int i = 1; i <= _n + 1; ++i) {
preg[i] = sta[getpos(i, [&](int x, int y) { return g[sta[x]] > g[y]; })];
while (*sta && g[i] > g[sta[*sta]]) --*sta;
sta[++*sta] = i;
}
rmq_f.init(_n, f, [&](pII x, pII y){
if (x.fi != y.fi) {
if (x.fi > y.fi) return x;
return y;
}
if (x.se < y.se) return x;
return y;
});
rmq_g.init(_n, g, [&](pII x, pII y){
if (x.fi != y.fi) {
if (x.fi > y.fi) return x;
return y;
}
if (x.se > y.se) return x;
return y;
});
int op, x, y;
while (q--) {
scanf("%d%d%d", &op, &x, &y);
if (op == 1) {
b[x + pre] = y;
seg[a[x + pre]].update(1, 1, m, in[_a[x + pre]], y);
} else {
printf("%d\n", max(getin(x + pre, y + pre), getout(x + pre, y + pre)));
}
}
}
return 0;
}