银联高校极客挑战赛 复赛


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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM