首页 > 其他分享 >P5513 [CEOI2013] Board 题解

P5513 [CEOI2013] Board 题解

时间:2023-12-27 10:24:01浏览次数:38  
标签:val int 题解 CEOI2013 P5513 mathcal 复杂度 define

P5513

容易发现,每次等价于对一个二进制数进行操作。但是这个二进制数长为 \(n\),即需要高精。但是这样支持加一和减一是复杂度会退化为 \(\mathcal{O}(n^2)\),有一个很正常的做法就压位,仿照 bitset 的做法进行操作,复杂度 \(\mathcal{O}(\frac{n ^ 2}{w})\)。

这样已经可以通过了,但发现乘除法就是单点赋值,加减法只需要找到最长的一段后缀 \(0/1\) 的位置并覆盖,可以使用线段树维护。找最长后缀串可以直接线段树上二分。时间复杂度线性对数。

如果觉得线段树上二分写起来有点麻烦的话可以直接在外面套一个二分,复杂度多出一只 \(\log\)。

最后统计答案是简单的,一定是到某一层后横向移动在到另一个点,枚举这个层即可。

代码(\(\mathcal{O}(n\log^2n)\)):

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define vi vector < int >
#define eb emplace_back
#define pii pair < int, int >
#define fi first
#define se second
#define TIME 1e3 * clock() / CLOCKS_PER_SEC
int Mbe;
mt19937_64 rng(35);
constexpr int N = 1e5 + 10, inf = 1e9;
int n, m, na, nb;
string a, b;
int val[N << 2], laz[N << 2];
void tag(int x, int v, int L, int R) {
	laz[x] = v;
	val[x] = (R - L + 1) * v;
}
void down(int x, int L, int R) {
	if(~laz[x]) {
		int m = (L + R) >> 1;
		tag(x << 1, laz[x], L, m);
		tag(x << 1 | 1, laz[x], m + 1, R);
		laz[x] = -1;
	}
}
void modify(int x, int L, int R, int l, int r, int v) {
	if(l <= L && R <= r) {
		tag(x, v, L, R);
		return;
	}
	down(x, L, R);
	int m = (L + R) >> 1;
	if(l <= m) modify(x << 1, L, m, l, r, v);
	if(r > m) modify(x << 1 | 1, m + 1, R, l, r, v);
	val[x] = val[x << 1] + val[x << 1 | 1];
}
int ask(int x, int L, int R, int l, int r) {
	if(l <= L && R <= r) return val[x];
	down(x, L, R);
	int m = (L + R) >> 1, res = 0;
	if(l <= m) res += ask(x << 1, L, m, l, r);
	if(r > m) res += ask(x << 1 | 1, m + 1, R, l, r);
	return res;
}
void query(int x, int L, int R, int type) {
	if(L == R) {
		if(type == 1) a[L] = val[x] + '0';
		else b[L] = val[x] + '0';
		return;
	}
	down(x, L, R);
	int m = (L + R) >> 1;
	query(x << 1, L, m, type);
	query(x << 1 | 1, m + 1, R, type);
}
int search(int R, int len, int type) {
	int l = 1, r = R;
	while(l <= r) {
		int m = (l + r) >> 1;
		if(ask(1, 1, len, m, R) == (R - m + 1) * type) r = m - 1;
		else l = m + 1;
	}
	return l;
}
void solve(int n, int type) {
	int now = 0;
	memset(laz, -1, sizeof(laz));
	memset(val, 0, sizeof(val));
	string s;
	if(type == 1) s = a;
	else s = b;
	for(int i = 1; i <= n; ++i) {
		if(s[i] == '1') {
			++now;
			modify(1, 1, n, now, now, 1);
		} else if(s[i] == '2') {
			++now;
			modify(1, 1, n, now, now, 2);
		} else if(s[i] == 'U') {
			modify(1, 1, n, now, now, 0);
			--now;
		} else if(s[i] == 'L') {
			int p = search(now, n, 1);
			if(p <= now) modify(1, 1, n, p, now, 2);
			modify(1, 1, n, p - 1, p - 1, 1);
		} else {
			int p = search(now, n, 2);
			if(p <= now) modify(1, 1, n, p, now, 1);
			modify(1, 1, n, p - 1, p - 1, 2);
		}
	}
	query(1, 1, n, type);
	if(type == 1) na = now;
	else nb = now;
}
int Med;
int main() {
	fprintf(stderr, "%.3lf MB\n", (&Mbe - &Med) / 1048576.0);
	ios :: sync_with_stdio(0);
	cin.tie(0); cout.tie(0);
	cin >> a >> b;
	n = a.size(), m = b.size();
	a = " " + a, b = " " + b;
	solve(n, 1);
	solve(m, 2);
	if(a > b) {
		swap(na, nb);
		swap(a, b);
	}
	int now = 0, ans = na + nb;
	for(int i = 1; i <= na; ++i) {
		if(now < inf) {
			now *= 2;
			if(a[i] == '1' && b[i] == '2') ++now;
			else if(a[i] == '2' && b[i] == '1') --now;
		}
		ans = min(ans, na - i + nb - i + now);
	}
	cout << ans << "\n";
	cerr << TIME << "ms\n";
	return 0;
}

标签:val,int,题解,CEOI2013,P5513,mathcal,复杂度,define
From: https://www.cnblogs.com/Pengzt/p/17929936.html

相关文章

  • 模拟赛简要题解
    11.16(C0389)100+10+50=160,rk3。本来BC都应该写出来的。A:dp或贪心都可以,贪心直接从下往上覆盖即可。B:注意:这里的\(\oplus\)指的是按位或。合法条件可以化简为:\(\oplus_{i=1}^{p}a_i=\oplus_{i=p+1}^{n}a_i\)。继续挖掘。看到位运算肯定想到拆位,考虑每一位第一次和......
  • 【数据结构】P4338 [ZJOI2018] 历史 题解
    P4338先考虑怎么安排崛起的先后顺序最优。但是发现好像没有一个很好的顺序去进行崛起,并且由于\(a_i\)的值域会很大,所以即使知道顺序应该也会难以进行维护。转换一下方向,正难则反。考虑每个点的贡献,但是颜色不同时只会算一次,所以要钦定是哪一个点造成的贡献。令当前考虑的点为......
  • CF1896D Ones and Twos 题解
    CF1896D如果只有单次询问其实可以双指针,但是这个难以进行拓展。考虑找点性质。发现\(a_i,v\in\{1,2\}\),从值域上下手。发现若存在和为\(S\)的方案,则一定有和为\(S-2\)的方案,因为可以直接\(-2\)或\(-1-1\)。然后就变为找最大的和为奇/偶数了,因为如果最大的都不行就肯定......
  • P9032 [COCI2022-2023#1] Neboderi 题解
    P9032考试题。发现\(g\)的值是若干个相同的段,且段数很少,因为每次取\(\gcd\)至少会将值域变为原来的一半。所以段数是\(\mathcal{O}(\logV)\)的。然后就可以从小到大枚举左端点,然后枚举\(g\)的值,找的是最远的满足\(\gcd(a_l,\dots,a_r)=g\)的\(r\),这里可以使用二分......
  • [CTSC2018]暴力写挂题解
    我们先将柿子变成\(\frac{1}{2}(dis_{x,y}+dep_{x}+dep_{y})-dep'_{lca'}\)考虑边分治,枚举断边,我们将一个点在第二棵树上的点权看成是\(v_x=d_x+dep_x\),答案就为\(v_x+v_y+dep'_{lca'}\)对于每次边分治将分治联通块内所有点在第二棵树上的建出虚树,同时将分治联通块以分治中心......
  • CF1887D Split 题解
    Problem-D-CodeforcesSplit-洛谷我现在水平好烂,再做下去自信心就全败没了先考虑\(Q=1\)怎么做?两种做法:暴力枚举分界点,左右判断暴力枚举\(\max\limits_{i=l}^{x}a_i\),找到最靠右边的分界点位置\(x\),判断是否\(\max\limits_{i=l}^{x}a_i<\min\limits......
  • [ABC267F] Exactly K Steps 题解
    [ABC267F]ExactlyKSteps题解思路首先发现,如果对于查询\((u,k),k>0\)可行,那么对于\((u,k-1)\)也一定可行,因为往回走一步就可以了,所以对于一个点可以找到离它最远的点,根据直径的结论,这个点一定是直径的端点之一。为了方便做,以直径的端点之一为根,然后写一个K级祖......
  • 【CF30E】Tricky and Clever Password 题解(manacher + exKMP)
    manacher+exKMP+二分。感觉是最粗暴的方法,想出来之后自己硬莽了4k,荣获题解区最长。Solution约定:下文所提及到的所有的回文串,均指奇长度回文串。显然把题目拆成两个部分,中间的回文串,以及两边相同的连续子串。考虑一下从哪个入手比较好。忘记是咋想的了,易得从两边相同......
  • [SNOI2019] 网络 题解
    [SNOI2019]网络题解最喜欢这道题。简要题意给一颗\(n\)个节点的树和一个参数\(d\),定义两个节点\(x,y\)之间的距离为\(x\)到\(y\)的简单路径上的边数。定义一个树上连通块的权值为连通块中任意两点的距离之和。定义一个树上连通块的直径为连通块中任意两点距离的最......
  • CF1887C Minimum Array 题解
    Problem-1887C-CodeforcesMinimumArray-洛谷有点被降智了/ll首先区间修改显然先转化成差分序列单点修改。显然对于相同的操作序列,\(a_i\)的取值对答案无影响,因此我们可以先让\(a_i\)全部取\(0\),最后再加回来即可假如说操作到某一时刻,\(a_i\)的值中第一个......