Educational Codeforces Round 166 (Rated for Div. 2)

写在前面

比赛地址:https://codeforces.com/contest/1976

满课,并且 48 小时之内只睡了 8h。

本来不想打的,但是手痒就上小号打了,然而唐唐唐掉大分呃呃

A

签到。

感谢 isdigit 函数。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
//=============================================================
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    int n; std::cin >> n;
    std::string s; std::cin >> s;
    int flag = 1;
    for (int i = 1; i < n; ++ i) {
      if (!isdigit(s[i - 1]) && isdigit(s[i])) flag = 0;
    }
    for (int i = 0, lst = -1; i < n; ++ i) {
      if (isdigit(s[i])) {
        if (lst == -1) lst = i;
        else if (s[lst] > s[i]) flag = 0;
        lst = i;
      }
    }
    for (int i = 0, lst = -1; i < n; ++ i) {
      if (!isdigit(s[i])) {
        if (lst == -1) lst = i;
        else if (s[lst] > s[i]) flag = 0;
        lst = i;
      }
    }
    std::cout << (flag ? "YES\n" : "NO\n");
  }
  return 0;
}

B

模拟。

\(a_1\sim a_n\) 的调整的代价是固定的,即为 \(\sum_{1\le i\le n} |a_i - b_i|\)。在对它们调整的过程中需要选择一个数复制一份变为 \(a_{n + 1}\),并最小化调整 \(a_{n + 1}\)\(b_{n + 1}\) 的代价 \(|b_{n + 1} - a_{n + 1}|\)

若存在 \(1\le i\le n\),使得 \(\min(a_i, b_i) \le b_{n + 1}\le \max(a_i, b_i)\),则 \(|b_{n + 1} - a_{n + 1}|\) 的值可以为 0;否则选择的被复制的数一定在集合 \(\{a_i\} \cup\{b_i\}\) 中,选择其中与 \(b_{n + 1}\) 最接近的数复制并计算代价即可。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
//=============================================================
int n, a[kN], b[kN];
//=============================================================
//=============================================================
int main() {
  //freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n;
    for (int i = 1; i <= n; ++ i) std::cin >> a[i];
    for (int i = 1; i <= n + 1; ++ i) std::cin >> b[i];
    LL ans = 0, flag = 0;
    for (int i = 1; i <= n; ++ i) {
      ans += abs(a[i] - b[i]);
      if (std::min(a[i], b[i]) <= b[n + 1] && b[n + 1] <= std::max(a[i], b[i])) flag = 1;
    }
    if (!flag) {
      LL delta = 1e9;
      for (int i = 1; i <= n; ++ i) {
        delta = std::min(delta, 1ll * abs(b[n + 1] - a[i]));
        delta = std::min(delta, 1ll * abs(b[n + 1] - b[i]));
      }
      ans += delta;
    }
    std::cout << ans + 1 << "\n";
  }
  return 0;
}

C

排序。

读错题了唐唐唐唐唐——获得了一道新题哈哈,什么时候需要就放上去。

首先假设 \(n + m + 1\) 个人全部到来,按照题意进行模拟找到其较优岗位并求得贡献之和。

然后按照到来顺序,分别枚举每个岗位中的人 \(i\),记该岗位人数上限为 \(k\)

  • 若这个人之前已经有 \(k\) 个人入职该岗位,则这个人不来贡献仅减少 \(\min(a_i, b_i)\)
  • 若这个人之前不到 \(k\) 个人入职该岗位,且以该岗位为较优岗位的人总数不大于 \(k\) 个,则这个人不来贡献减少 \(\max(a_i, b_i)\)
  • 否则这个不来一定会使得第 \(k\) 个人入职该岗位,则贡献变化量为 \(-\max(a_i, b_i) + \max(a_{k}, b_{k}) - \min(a_k, b_k)\)

讨论即可,总时间复杂度 \(O(n+m)\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
#define pii std::pair<int,int>
#define mp std::make_pair
const int kN = 2e5 + 10;
//=============================================================
int n, m;
LL sum, ans[kN];
std::vector<int> aa, bb;
int a[kN], b[kN], c[kN];
//=============================================================
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  std::ios::sync_with_stdio(0), std::cin.tie(0);
  int T; std::cin >> T;
  while (T --) {
    std::cin >> n >> m;
    sum = 0;
    aa.clear(), bb.clear();

    for (int i = 1; i <= n + m + 1; ++ i) std::cin >> a[i];
    for (int i = 1; i <= n + m + 1; ++ i) {
      std::cin >> b[i];
      c[i] = abs(a[i] - b[i]); 
      sum += std::min(a[i], b[i]);
    }

    int cnta = 0, cntb = 0;
    for (int i = 1; i <= n + m + 1; ++ i) {
      if (a[i] > b[i]) {
        ++ cnta;
        if (cnta <= n) sum += a[i] - b[i];
        aa.push_back(i);
      } else {
        ++ cntb;
        if (cntb <= m) sum += b[i] - a[i];
        bb.push_back(i);
      }
    }

    for (int i = aa.size() - 1; i >= n; -- i) ans[aa[i]] = sum - b[aa[i]];
    if ((int) aa.size() <= n) {
      for (int i = 0; i < (int) aa.size(); ++ i) ans[aa[i]] = sum - a[aa[i]];
    } else {
      for (int i = n - 1; i >= 0; -- i) ans[aa[i]] = sum - a[aa[i]] + c[aa[n]];
    }

    for (int i = bb.size() - 1; i >= m; -- i) ans[bb[i]] = sum - a[bb[i]];
    if ((int) bb.size() <= m) {
      for (int i = 0; i < (int) bb.size(); ++ i) ans[bb[i]] = sum - b[bb[i]];
    } else {
      for (int i = m - 1; i >= 0; -- i) ans[bb[i]] = sum - b[bb[i]] + c[bb[m]];
    }
    
    for (int i = 1; i <= n + m + 1; ++ i) std::cout << ans[i] << " ";
    std::cout << "\n";
  }
  return 0;
}

D

括号序列,枚举。

好玩题,场上一直想着怎么拆成两半拼起来了,还能这么想的 oonp。

首先考虑如果选出了一段区间 \([l, r]\) 并将其逐字符取反,如何判断取反后整个括号序列是否合法。一个显然的想法是将 \([1, l - 1]\)\([l, r]\)\([r + 1, n]\) 三段括号串分别先进行括号匹配将它们化简,然后检查化简后的三段能否匹配。套路地考虑将左右括号分别看做 \(\plusmn 1\),以 (()())(()) 为例则有如下的折线图:

图片来自:https://www.cnblogs.com/extractstars/p/18223651

发现区间 \([l, r]\) 取反相当于将选中的折线 \((l - 1)\rightarrow r\) 中各段进行翻折。为了保证翻折后折线仍然是连续的,且不会翻折到 \(x\) 轴下方,记位置 \(i\) 处折线高度为 \(h_i\),则需要满足:

\[\begin{aligned} h_{l - 1} &= h_{r}\\ \max_{l\le i\le r - 1}h_{i} &\le 2\times h_{l - 1} \end{aligned}\]

上述 \(h\) 数组可以在括号匹配过程中直接求得,则一个显然的想法是按照值递减枚举 \(h_{l - 1} = h_r\),再顺序枚举对应的每个位置,然后考虑每个位置与之前多少位置可以构成合法的修改区间。发现仅需检查其中是否有不满足条件 2 的位置即可,考虑双指针枚举 \(\ge 2\times h_{l - 1}\) 的所有位置并使用 set 记录,求合法位置对时同样双指针求得满足条件 2 的位置的合法对即可。

总时间复杂度 \(O(n\log n)\) 级别。

//
/*
By:Luckyblock
*/
#include <bits/stdc++.h>
#define LL long long
const int kN = 2e5 + 10;
int n, pre[kN];
std::string s;
LL ans;
std::vector<int> pos[kN];
std::set<int> high;

void solve() {
	n = s.length();
	high.clear();
	for (int i = 0; i < n; ++ i) {
		pre[i + 1] = pre[i] + (s[i] == '(' ? 1 : -1);
		pos[i + 1].clear();
	}
	for (int i = 1; i <= n; ++ i) pos[pre[i]].push_back(i);
	
	for (int i = n, j = n; i; -- i) {
		while (j > 2 * i) {
			for (auto p: pos[j]) high.insert(p);
			-- j;
		}
		for (int k = 1, sz = pos[i].size(), cnt = 1; k < sz; ++ k) {
			auto it = high.upper_bound(pos[i][k - 1]);
			if (it == high.end() || *it > pos[i][k]) {
				ans += cnt;
			} else {
				cnt = 0;
			}
			++ cnt;
		}
	}
}

int main() {
	int T; std::cin >> T;
	while (T --) {
		std::cin >> s;
		ans = 0;
		solve();
		std::cout << ans << "\n";
	}
}
/*
1
()()()()

1
()((()))
10123210

(()(()))
()(()())
*/

E

写在最后

参考:

学到了什么:

  • D:括号序列的又一种数形结合的模型转换。
posted @ 2024-05-31 12:06  Luckyblock  阅读(261)  评论(0编辑  收藏  举报