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;
}