Educational Codeforces Round 174 (Rated for Div. 2)


A. Was there an Array?

题意:一个长度为na数组,变成了长度为n2b数组,对于每个i[2,n1],如果ai==ai1&&ai==ai+1bi1=1,否则等于0。现在给你一个b数组,判断有没有一个a数组可以变成b

如果b中有1,0,1这个子数组,那么一定没有对应的a数组,因为ai1=ai=ai+1,ai+1=ai+2=ai+3可以得出这几个都相等,但中间有个零,告诉你有三个数不都相同,产生矛盾。否则一定可以,因为两个1之间有两个以上的零是可以构造方案的,其他情况也是合法的。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n - 2);
    for (int i = 0; i < n - 2; ++ i) {
    	std::cin >> a[i];
    }

    for (int i = 0; i + 2 < n - 2; ++ i) {
    	if (a[i] == 1 && a[i + 1] == 0 && a[i + 2] == 1) {
    		std::cout << "NO\n";
    		return;
    	}
    }

    std::cout << "YES\n";
}

B. Set of Strangers

题意:给你一个矩阵,你每次可以选择一些元素,使得它们的值都相同且没有两个元素相邻,然后把他们变成另一种元素,问让整个矩阵相同的最小操作数。

发现同一种颜色最多操作两次,如果这个颜色每个元素都不相邻,那么只需要一次就可以全部变,如果有相邻点,那么可以取i+j是偶数的位置一组,和i+j是奇数的位置一组,这两类的每一类中的元素一定不相邻,所有最多两次操作。

于是把每个元素的操作数存下来,排序后除最后一个不选,其他颜色的操作数都算上。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector a(n, std::vector<int>(m));
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		std::cin >> a[i][j];
    		-- a[i][j];
    	}
    }

    const int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
    std::vector<int> cnt(n * m);
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		if (cnt[a[i][j]] < 2) {
    			cnt[a[i][j]] = 1;
    			for (int k = 0; k < 4; ++ k) {
    				int x = i + dx[k], y = j + dy[k];
    				if (x < 0 || x >= n || y < 0 || y >= m) {
    					continue;
    				}

    				if (a[i][j] == a[x][y]) {
    					cnt[a[i][j]] = 2;
    				}
    			}
    		}
    	}
    }

    std::vector<int> b;
    for (int i = 0; i < n * m; ++ i) {
    	if (cnt[i]) {
    		b.push_back(cnt[i]);
    	}
    }

    std::sort(b.begin(), b.end());
    int ans = 0;
    for (int i = 0; i + 1 < b.size(); ++ i) {
    	ans += b[i];
    }

    std::cout << ans << "\n";
}

C. Beautiful Sequence

题意:给你一个数组,只有1,2,3三种值,你要选以1开头,3结尾,中间若干个2的子序列,求有多少个。

预处理pre1,suf2,suf3,,表示1的个数的前缀和,2的个数的后缀和,3的个数的后缀和。然后枚举每个2,只选这个2在中间就有pre1i×suf3i种选法,然后考虑在前面选若干个2,设j<i,aj==2,前面每个21的后面的选法有pre1j个,但两个2中间有一些2这些2可选可不选,一共2suf2jsuf2i1种选法,但发现不好处理,可以直接算是2suf2j1个,那么前面总共是jpre1j×2suf2j1×suf3i的贡献,考虑消除重复的贡献,直接都除上2suf2i就行了,jpre1j×2suf2j1×suf3i2suf2i=jpre1j×2suf2jsuf2i1×suf3i,正好是我们所求的。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    std::vector<int> pre1(n + 1), suf2(n + 2), suf3(n + 2);
    for (int i = 0; i < n; ++ i) {
    	pre1[i + 1] = pre1[i] + (a[i] == 1);
    }

    for (int i = n - 1; i >= 0; -- i) {
    	suf2[i + 1] = suf2[i + 2] + (a[i] == 2);
    	suf3[i + 1] = suf3[i + 2] + (a[i] == 3);
    }

    Z ans = 0, pre = 0;
    for (int i = 1; i <= n; ++ i) {
    	if (a[i - 1] == 2) {
    		ans += (Z)pre1[i] * suf3[i] + pre / power<Z>(2, suf2[i]) * suf3[i];
    		pre += pre1[i] * power<Z>(2, suf2[i] - 1);
    	}
    }

    std::cout << ans << "\n";
}

D. Palindrome Shuffle

题意:给你一个字符串,你可以进行一次操作,重新排列一个区间,使得字符串是回文串,求区间最短长度。

ls[1,l1]!=s[nl+1,n]的最小的位置,rs[r,n/2]!=s[n/2+1,nr+1]的最大的位置,那么看s[l...r]s[nr+1...nl+1]的字符数是不是相等,如果相等,则可以重排s[l..r]使得字符串是回文串。
否则我们可以二分往中间左边走几步和往中间右边走几步,看重排这个区间能否是回文串,check就是先判断对称的另一边没被选择的位置上的字符在这个区间是不是足够,然后剩下的一定要是偶数,因为要左边一个右边一个。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();
    std::vector<std::array<int, 26>> sum(n + 1);
    for (int i = 0; i < n; ++ i) {
    	sum[i + 1] = sum[i];
    	sum[i + 1][s[i] - 'a'] += 1;
    }

    int l = 0;
    while (l < n / 2 && s[l] == s[n - 1 - l]) {
    	++ l;
    }

    if (l == n / 2) {
    	std::cout << 0 << "\n";
    	return;
    }

    int r = n / 2 - 1;
    while (r > l && s[r] == s[n - 1 - r]) {
    	-- r;
    }

    bool flag = true;
    ++ l, ++ r;
    for (int i = 0; i < 26; ++ i) {
    	if (sum[r][i] - sum[l - 1][i] != sum[n - l + 1][i] - sum[n - r][i]) {
    		flag = false;
    		break;
    	}
    }

    if (flag) {
    	std::cout << r - l + 1 << "\n";
    } else {
    	auto check1 = [&](int k) -> bool {
    		std::array<int, 26> cnt{};
    		for (int i = 0; i < 26; ++ i) {
    			cnt[i] = sum[n / 2 + k][i] - sum[l - 1][i];
    		}

    		for (int i = (n - l + 1); i > n / 2 + k; -- i) {
    			if ( -- cnt[s[i - 1] - 'a'] < 0) {
    				return false;
    			}
    		}

    		for (int i = 0; i < 26; ++ i) {
    			if (cnt[i] & 1) {
    				return false;
    			}
    		}

    		return true;
    	};

    	int L = 1, R = (n - l + 1) - n / 2;
    	while (L < R) {
    		int mid = L + R >> 1;
    		if (check1(mid)) {
    			R = mid;
    		} else {
    			L = mid + 1;
    		}
    	}

    	int ans = L + n / 2 - l + 1;
    	auto check2 = [&](int k) -> bool {
    		std::array<int, 26> cnt{};
    		for (int i = 0; i < 26; ++ i) {
    			cnt[i] = sum[n - l + 1][i] - sum[n / 2 - k][i];
    		}

    		for (int i = l; i < n / 2 - k + 1; ++ i) {
    			if ( -- cnt[s[i - 1] - 'a'] < 0) {
    				return false;
    			}
    		}

    		for (int i = 0; i < 26; ++ i) {
    			if (cnt[i] & 1) {
    				return false;
    			}
    		}

    		return true;
    	};

    	L = 1, R = n / 2 - l + 1;
    	while (L < R) {
    		int mid = L + R >> 1;
    		if (check2(mid)) {
    			R = mid;
    		} else {
    			L = mid + 1;
    		}
    	}

    	ans = std::min(ans, L + (n - l + 1) - n / 2);
    	std::cout << ans << "\n";
    }
}

E. A, B, AB and BA

题意:给你一个AB串,你可以把其分成不超过aAbBabABbaBA。问能否将这个字符串分隔成功。

赛后补题。
这题也是一个思维题,属于是找到思路后仔细想应该就能想出来做法的,但这题步骤比较多,导致评价为依托。我也是看了别人的题解。
因为我们没有AA,BB这样的分隔方式,所以我们可以将所有子串分成四类:

  1. 长度为偶数以A开头:ABA...BAB
  2. 长度为奇数以A开头:ABAB..ABA
  3. 长度为偶数以B开头:BAB...ABA
  4. 长度为奇数以B开头:BABA..BAB

一个显然的贪心是,我们应该先用ab,ba,因为a,b的使用没有限制。那么我们可以先处理偶数长度的,按照开头字母用ab,ba分别处理。然后可能会有一些剩下的,就用另一种去处理,这样一定会剩下至少两个,用a,b去操作即可。然后再去处理奇数长度的,发现奇数长度的不管以哪个字母开头,ab,ba去处理都会至少留下一个,于是枚举ab,ba分别处理这两种,然后剩下的也给a,b处理即可。然后看有没有剩下的,全给a,b处理。
一个需要注意的地方是,按照上面顺序处理的过程中,我们应该先处理长度小的,因为假设我们用ab处理了一些长度更长的奇数段,然后剩下的都是ABAB类的偶数段,如果这时已经没有ab了,显然这些无法处理,但如果我们先处理这些,那么那些更长的可以用ba去处理。

点击查看代码
void solve() {
    std::string s;
    std::cin >> s;
    int n = s.size();
    int cnt1[2]{}, cnt2[2]{};
    std::cin >> cnt1[0] >> cnt1[1] >> cnt2[0] >> cnt2[1];
    std::vector<int> even[2], odd[2];
    for (int l = 0, r = 1; r <= n; ++ r) {
    	if (r == n || s[r] == s[r - 1]) {
    		if (r - l & 1) {
    			odd[s[l] - 'A'].push_back(r - l);
    		} else {
    			even[s[l] - 'A'].push_back(r - l);
    		}

    		l = r;
    	}
    }

    for (int i = 0; i < 2; ++ i) {
    	std::sort(odd[i].begin(), odd[i].end(), std::greater<int>());
    	std::sort(even[i].begin(), even[i].end(), std::greater<int>());
    }

    for (int i = 0; i < 2; ++ i) {
    	while (cnt2[i] && even[i].size()) {
    		int m = even[i].back(); even[i].pop_back();
    		int k = std::min(m / 2, cnt2[i]);
    		cnt2[i] -= k; m -= k * 2;
    		if (m) {
    			even[i].push_back(m);
    		}
    	}
    }

    for (int i = 0; i < 2; ++ i) {
    	while (cnt2[i ^ 1] && even[i].size()) {
    		int m = even[i].back(); even[i].pop_back();
    		int k = std::min(m / 2 - 1, cnt2[i ^ 1]);
    		cnt2[i ^ 1] -= k; m -= k * 2;
    		if (m) {
    			cnt1[i] -= m / 2;
    			cnt1[i ^ 1] -= m / 2;
    		}
    	}
    }

    for (int i = 0; i < 2; ++ i) {
    	for (int j = 0; j < 2; ++ j) {
    		while (cnt2[i] && odd[j].size()) {
    			int m = odd[j].back(); odd[j].pop_back();
    			int k = std::min(cnt2[i], m / 2);
    			cnt2[i] -= k; m -= k * 2;
    			if (m > 1) {
    				odd[j].push_back(m);
    			} else {
    				cnt1[j] -= 1;
    			}
    		}
    	}
    }

   	for (int i = 0; i < 2; ++ i) {
   		while (even[i].size()) {
   			int m = even[i].back(); even[i].pop_back();
   			cnt1[0] -= m / 2;
   			cnt1[1] -= m / 2;
   		}

   		while (odd[i].size()) {
   			int m = odd[i].back(); odd[i].pop_back();
   			cnt1[i] -= m / 2 + 1;
   			cnt1[i ^ 1] -= m / 2;
   		}
   	}

   	if (cnt1[0] < 0 || cnt1[1] < 0) {
   		std::cout << "NO\n";
   	} else {
   		std::cout << "YES\n";
   	}
}
posted @   maburb  阅读(296)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示