Codeforces Round #687 (Div. 2) 11/29

比赛链接

T1

T组输入,每组输入地图的大小 n ∗ m n*m nm,并且地图上每一个点都存在一个犯人,每个犯人可以花费一个晚上走到相邻的四个格子,现在(x,y)处有一个逃生通道,询问最后一个走的犯人需要花费几个夜晚到达。

判断地图的四个端点寻找距离最大者即可。

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;	while (b) { if (b & 1)	ans *= a;		b >>= 1;		a *= a; }	return ans; }	ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 7;

int n, m;
int cnt[10];
void solve() {
	int n = read(), m = read(), a = read(), b = read();
	cnt[1] = a - 1 + b - 1;
	cnt[2] = n - a + b - 1;
	cnt[3] = a - 1 + m - b;
	cnt[4] = n - a + m - b;
	int ans = 0;
	for (int i = 1; i <= 4; ++i)
		ans = max(ans, cnt[i]);
	print(ans);
}

int main() {
	int T = read();	while (T--)
		solve();
	return 0;
}

T2

给出一个长度为n的颜色数组,对应每个颜色值是 a i a_i ai,每次你可以操作长度为m的连续子数组,把他们的颜色改成一个相同的值。询问你把全部的数改成同一个颜色的最小操作次数是几次。
n , m ≤ 1 0 5 , a i ≤ 100 n,m\leq10^5,a_i\leq100 n,m105,ai100

数据范围比较友好,暴力遍历全部出现的颜色,再判断一下如果使用这个颜色需要修改几次。

const int N = 1e6 + 7;
 
int n, m;
int a[N], b[N];
void solve() {
	int maxi = 0, mini = INF;
	n = read(), m = read();
	unordered_set<int> st;
	for (int i = 1; i <= n; ++i) {
		a[i] = read();
		st.insert(a[i]);
	}
	int ans = INF;
	for(auto& i : st){
		int cnt = 0;
		for (int j = 1; j <= n; ++j) {
			if (a[j] == i)	continue;
			else {
				++cnt;
				j += m-1;
			}
		}
		ans = min(ans, cnt);
	}
	print(ans);
}

T3

跳板模型,给你长度为n的01串,0代表这个位置没有跳板,1代表这个位置有跳板。你一开始会把球扔在下标为p(从1开始计算下标),并且下次球会弹跳到p+k处,在下次就会到p+2*k处,直到弹出n外面。现在如果一个落点没有跳板,那么你的球就死了,现在你可以在任意位置添加一块跳板花费的金钱是x,你也可以移除最开头的跳板花费是y,并且移除操作只可以对开头连续的串进行。询问你要成功活着跳到n外面去的最小花费是多少。
1 ≤ p ≤ n ≤ 1 0 5 , 1 ≤ k ≤ n , 1 ≤ x , y ≤ 1 0 4 1\leq p\leq n\leq10^5 , 1\leq k\leq n, 1\leq x,y\leq 10^4 1pn105,1kn,1x,y104

首先如果我们不看可以移除开头格子的话,使用倒序的一个dp递推,可以求到每个位置后面最少要添加几块跳板。接下来我们就再去枚举我们移除开头几个格子就行了,移除i个格子的话,说明我们的起始落点就变成p+i了,但是注意的是,题目没说可以移除n-p+1个格子。也就是直接丢出去,不经历一次跳板是不合法的,因为这个一直wa。

const int N = 1e6 + 7;
 
char s[N];
ll dp[N];
 
void solve() {
	ll n = read(), p = read(), k = read();
	scanf("%s", s + 1);
	ms(dp, 0); // 求以i为落点,后面还需要添加几个跳板
	for (ll i = 1; i <= k; ++i) {
		dp[n - i + 1] = (s[n - i + 1] == '0');
	}
	for (ll i = n - k; i; --i) {
		dp[i] = (s[i] == '0') + dp[i + k];
	}
	ll x = read(), y = read();
	ll ans = 1e18;
	for (ll i = 0; i <= n - p; ++i) { // 枚举去掉的开头跳板
		ll tmp = y * i;
		if (i + p <= n)	tmp += x * dp[i + p];
		ans = min(ans, tmp);
	}
	print(ans);
}

T4

起始给出长度为n的非递减序列,你每次可以选择两个相邻的元素进行异或操作,并且把两数异或的结果放入之前位置,但是拿走两个数只放回一个数。询问你如果要把序列变得不是非递减的最少要进行几次异或操作,如果永远都无法变得不是非递减输出-1。
2 ≤ n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 9 2\leq n\leq10^5,1\leq a_i\leq10^9 2n105,1ai109

首先我们看异或题,都尽可能转换成二进制考虑一下,如果我们使用 b i b_i bi记录每个位置的二进制最高次,如果出现了三个连续的数他们的二进制最高次幂是相同的,也就是 b i − 1 = b i = b i + 1 b_{i-1}=b_i=b_{i+1} bi1=bi=bi+1这样的三个点,我们可以选择 a i ⊕ a i + 1 a_i\oplus a_{i+1} aiai+1这样处理完之后他们的之前最高次一定是0了,一定会保证 a i − 1 > a i ⊕ a i + 1 a_{i-1}>a_i\oplus a_{i+1} ai1>aiai+1。我们可以发现大概有31位二进制位,如果大于了62位左右的话一定会有3个的相同二进制位,这样我们直接输出1就可以了。那么接下来的对于 n ≤ 70 n\leq70 n70的,直接暴力枚举全部可能的区间,可以使用异或前缀和加速一下。

const int N = 1e5 + 7;

ll n, m;
int pre[N]; // 前缀异或和
void solve() {
	n = read();
	rep(i, 1, n)	pre[i] = pre[i - 1] ^ read();
	if (n == 2) { puts("-1"); return; }
	if (n >= 70) { print(1); return; }
	int ans = INF;
	rep(i, 1, n - 1) {
		rep(j, i + 1, n) {
			rep(k, i, j - 1) {
				int tmp1 = pre[k] ^ pre[i - 1];
				int tmp2 = pre[j] ^ pre[k];
				if (tmp1 > tmp2)	ans = min(ans, j - i - 1); // 合并成2个数
				int tmp3 = tmp1 ^ tmp2;
				if (i != 1 and tmp3 < (pre[i - 1] ^ pre[i - 2]))
					ans = min(ans, j - i); // 多花一次合并分隔的两数
				if (j != n and tmp3 > (pre[j + 1] ^ pre[j]))
					ans = min(ans, j - i);
			}
		}
	}
	if (ans == INF)	print(-1);
	else print(ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值