首页 > 其他分享 >AtCoder Regular Contest 132 D Between Two Binary Strings

AtCoder Regular Contest 132 D Between Two Binary Strings

时间:2023-05-22 18:34:40浏览次数:40  
标签:AtCoder typedef Binary int Contest long maxn define

洛谷传送门

AtCoder 传送门

提供一个 dp 思路。

下文设串长为 \(n\),串中 \(1\) 个数为 \(m\)。

考虑如何求 \(d(s, t)\)。设 \(s\) 的 \(1\) 位置分别为 \(a_1, a_2, ..., a_m\),\(t\) 的 \(1\) 位置分别为 \(b_1, b_2, ..., b_m\)。那么 \(d(s, t) = \sum\limits_{i=1}^m |a_i - b_i|\)。

更进一步地,对于串 \(s'\),设 \(s'\) 的 \(1\) 位置分别为 \(c_1, c_2, ..., c_m\),那么 \(\forall i \in [1, m], c_i \in [\min(a_i, b_i), \max(a_i, b_i)]\) 是 \(d(s, s') = d(s', t)\) 的充要条件。设 \(l_i = \min(a_i, b_i), r_i = \max(a_i, b_i)\)。

考虑转化题中的 "beauty",相当于最小化极长同字符连续段个数。

考虑 dp。设 \(f_i\) 表示填完前 \(i\) 个 \(1\) 的最小极长连续段个数。枚举 \(j\) 表示第 \(j \sim i\) 个 \(1\) 放一起,合法当且仅当 \(l_i - r_j + 1 \le i - j + 1 \le r_i - l_j + 1\),此时有转移 \(f_i = f_{j-1} + 2\),表示先加一段 \(0\) 再加一段 \(1\)。

注意处理开头一段是 \(1\) 和末尾一段是 \(1\) 的情况。

感性理解,对于每个 \(i\),合法的 \(j\) 一定形成一个区间,因为不可能 \(j_1 \sim i\) 能放一起但是 \(j_2 \sim i\) 不能(\(j_1 < j_2\))。可以二分得出左端点,然后使用线段树优化。可以做到 \(O(n \log n)\),已经可以通过。

更进一步,发现对于每个 \(i\),最左合法转移点单调不降。并且由于这个性质,\(f_i\) 单调不降。可以双指针维护每个 \(i\) 的最左合法转移点,然后 \(O(1)\) 转移。总时间复杂度降至 \(O(n)\)。

$O(n \log n)$ 的 code
// Problem: D - Between Two Binary Strings
// Contest: AtCoder - AtCoder Regular Contest 132
// URL: https://atcoder.jp/contests/arc132/tasks/arc132_d
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 300100;
const int inf = 0x3f3f3f3f;

int n, m, f[maxn];
char s[maxn], t[maxn];
struct node {
	int l, r;
	node(int a = 0, int b = 0) : l(a), r(b) {}
} a[maxn];

namespace SGT {
	int tree[maxn << 2];
	
	inline void init() {
		mems(tree, 0x3f);
	}
	
	inline void pushup(int x) {
		tree[x] = min(tree[x << 1], tree[x << 1 | 1]);
	}
	
	void update(int rt, int l, int r, int x) {
		if (l == r) {
			tree[rt] = f[x];
			return;
		}
		int mid = (l + r) >> 1;
		(x <= mid) ? update(rt << 1, l, mid, x) : update(rt << 1 | 1, mid + 1, r, x);
		pushup(rt);
	}
	
	int query(int rt, int l, int r, int ql, int qr) {
		if (ql > qr) {
			return inf;
		}
		if (ql <= l && r <= qr) {
			return tree[rt];
		}
		int mid = (l + r) >> 1, res = inf;
		if (ql <= mid) {
			res = min(res, query(rt << 1, l, mid, ql, qr));
		}
		if (qr > mid) {
			res = min(res, query(rt << 1 | 1, mid + 1, r, ql, qr));
		}
		return res;
	}
}

void solve() {
	scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
	if (!n || !m) {
		printf("%d\n", n + m - 1);
		return;
	}
	n += m;
	vector<int> va, vb;
	for (int i = 1; i <= n; ++i) {
		if (s[i] == '1') {
			va.pb(i);
		}
		if (t[i] == '1') {
			vb.pb(i);
		}
	}
	for (int i = 0; i < m; ++i) {
		int x = va[i], y = vb[i];
		if (x > y) {
			swap(x, y);
		}
		a[i + 1] = node(x, y);
		// printf("l, r: %d %d\n", x, y);
	}
	SGT::init();
	mems(f, 0x3f);
	f[0] = 0;
	SGT::update(1, 0, m, 0);
	for (int i = 1; i <= m; ++i) {
		if (a[1].l == 1 && a[i].l <= i && i <= a[i].r) {
			f[i] = 1;
		}
		if (a[i].l - a[1].r + 1 <= i && i <= a[i].r - a[1].l + 1) {
			f[i] = min(f[i], 2);
		}
		int l = 2, r = i, pos = i + 1;
		while (l <= r) {
			int mid = (l + r) >> 1;
			if (a[i].l - a[mid].r + 1 <= i - mid + 1 && i - mid + 1 <= a[i].r - a[mid].l + 1) {
				pos = mid;
				r = mid - 1;
			} else {
				l = mid + 1;
			}
		}
		f[i] = min(f[i], SGT::query(1, 0, m, pos - 1, i - 1) + 2);
		// for (int j = i; j > 1; --j) {
			// if (a[i].l - a[j].r + 1 <= i - j + 1 && i - j + 1 <= a[i].r - a[j].l + 1) {
				// f[i] = min(f[i], f[j - 1] + 2);
			// } else {
				// break;
			// }
		// }
		SGT::update(1, 0, m, i);
	}
	int ans = f[m] + 1;
	for (int i = m; i; --i) {
		if (a[m].r == n && a[i].l <= n - (m - i) && n - (m - i) <= a[i].r) {
			ans = min(ans, f[i - 1] + 2);
		}
	}
	printf("%d\n", n - ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

$O(n)$ 的 code
// Problem: D - Between Two Binary Strings
// Contest: AtCoder - AtCoder Regular Contest 132
// URL: https://atcoder.jp/contests/arc132/tasks/arc132_d
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;

const int maxn = 300100;
const int inf = 0x3f3f3f3f;

int n, m, f[maxn];
char s[maxn], t[maxn];
struct node {
	int l, r;
	node(int a = 0, int b = 0) : l(a), r(b) {}
} a[maxn];

void solve() {
	scanf("%d%d%s%s", &n, &m, s + 1, t + 1);
	if (!n || !m) {
		printf("%d\n", n + m - 1);
		return;
	}
	n += m;
	vector<int> va, vb;
	for (int i = 1; i <= n; ++i) {
		if (s[i] == '1') {
			va.pb(i);
		}
		if (t[i] == '1') {
			vb.pb(i);
		}
	}
	for (int i = 0; i < m; ++i) {
		int x = va[i], y = vb[i];
		if (x > y) {
			swap(x, y);
		}
		a[i + 1] = node(x, y);
	}
	mems(f, 0x3f);
	f[0] = 0;
	for (int i = 1, j = 1; i <= m; ++i) {
		if (a[1].l == 1 && a[i].l <= i && i <= a[i].r) {
			f[i] = 1;
		}
		if (a[i].l - a[1].r + 1 <= i && i <= a[i].r - a[1].l + 1) {
			f[i] = min(f[i], 2);
		}
		while (j <= i && !(a[i].l - a[j].r + 1 <= i - j + 1 && i - j + 1 <= a[i].r - a[j].l + 1)) {
			++j;
		}
		if (j <= i) {
			f[i] = min(f[i], f[j - 1] + 2);
		}
	}
	int ans = f[m] + 1;
	for (int i = m; i; --i) {
		if (a[m].r == n && a[i].l <= n - (m - i) && n - (m - i) <= a[i].r) {
			ans = min(ans, f[i - 1] + 2);
		}
	}
	printf("%d\n", n - ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:AtCoder,typedef,Binary,int,Contest,long,maxn,define
From: https://www.cnblogs.com/zltzlt-blog/p/17421412.html

相关文章

  • 2023 Xian Jiaotong University Programming Contest
    A.大水题#include<bits/stdc++.h>#include<ext/rope>#include<ext/pb_ds/assoc_container.hpp>usingnamespacestd;usingnamespace__gnu_cxx;usingnamespace__gnu_pbds;#definefifirst#definesesecond#definelcu<<1#define......
  • AtCoder Beginner Contest 302 Ex Ball Collector
    洛谷传送门AtCoder传送门考虑如果只询问一次怎么做。连边\((a_i,b_i)\),对于每个连通块分别考虑。这是ARC111B,如果一个连通块是树,肯定有一个点不能被选;否则有环,一定能构造一种方案,使得每个点都被选。那么现在对于每个点都要求,考虑dfs时合并当前的\((a_u,b_u)\),并且使用......
  • 【题解】Atcoder ABC302 F,G,Ex
    完全不会G和Ex,这些套路还是要积累一下的。F.MergeSet题目描述:给定\(n\)个集合,每次可以合并两个有交的集合,问最少多少次合并可以让\(1\)和\(m\)位于同一个集合中。题目分析:一开始将题读成了将\([1,m]\)位于同一个集合中,然后就感觉这个题相当不可做。因为集合的元......
  • AtCoder Regular Contest 130 E Increasing Minimum
    这题太神仙了吧!感觉还不是很懂,但是尽力理一下思路。设\(t_x\)为最大的\(j\)使得\(i_j=x\),不存在则\(t_x=0\)。设\(1\simn\)的数按照\(t\)从小到大排序后是\(p_1,p_2,...,p_n\),设\(q_i\)为\(i\)在\(p\)中的排名,即\(q_{p_i}=i\)。发现正着不好考虑,......
  • AtCoder Beginner Contest 302
    A-Attack(abc302a)题目大意给定怪物的血量\(a\)和你每次攻击扣除的血量\(b\),问打多少次怪物才会死。解题思路答案即为\(\lceil\frac{a}{b}\rceil=\lfloor\frac{a+b-1}{b}\rfloor\)神奇的代码#include<bits/stdc++.h>usingnamespacestd;usingLL=long......
  • AtCoder Regular Contest 130 C Digit Sum Minimization
    洛谷传送门AtCoder传送门分类讨论,但是写起来挺答辩的。首先发现我们要使进位尽量多。特判怎么重排都没有进位的情况(\(a_i+b_i<10\))。然后枚举个位选的两个数字,并且要求它们和\(\ge10\)。如果当前位两个位都有数字,那么从小到大枚举数位的和\(\in[9,18]\);如果有数字......
  • 1102 Invert a Binary Tree
    题目:ThefollowingisfromMaxHowell@twitter:Google:90%ofourengineersusethesoftwareyouwrote(Homebrew),butyoucan'tinvertabinarytreeonawhiteboardsofuckoff. Nowit'syourturntoprovethatYOUCANinvertabinarytree!I......
  • 2023 Hubei Provincial Collegiate Programming Contest
    C.DarknessI首先根据短边放一条对角线,然后往后每隔一列只放一个即可。#include<bits/stdc++.h>usingnamespacestd;#defineintlonglongint32_tmain(){ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);intn,m;cin>>n>>m......
  • 2023 CCPC Henan Provincial Collegiate Programming Contest
    A.小水獭游河南a的长度小于26,所以直接暴力枚举暴力判断。#include<bits/stdc++.h>usingnamespacestd;voidsolve(){strings;cin>>s;if(s.size()==1){cout<<"NaN\n";return;}map<char,int>cnt;......
  • AtCoder Regular Contest 133 E Cyclic Medians
    洛谷传送门AtCoder传送门其实是套路题,但是为什么做不出来啊第一步就是经典套路。枚举\(k\),统计中位数\(>k\)的方案数,加起来就是中位数的总和。那么现在\(x_{1\simn},y_{1\simm}\)就变成了\(0/1\)序列,考虑一次操作,如果\((x,y)=(0,0)\),那么\(a\)会变成\(0\)......