Codeforces Round #531 (Div. 3)题解

C.Doors Breaking and Repairing

题意:有n个门以及他们的血量,A的能力值x和B的能力值y,和无限个回合,每回合先由A选择一个门进行攻击,选择的门血量减少x,然后由B选择一个门进行加固,血量增加y(一个门的血量(leq 0)以后他就不能被加固了,但即使一扇门没有被攻击过,也可以加固)

问最后A最多能打破多少扇门

如果 (x > y),所有门的总血量每回合都会减少,经过无限个回合,所有的门一定都会被打破。

否则就是 (x leq y) 的情况,发现那些血量 (> x) 的门A永远打不破,因为A打一下,B就可以加固那扇门,血量 (leq x) 的门,A打破一扇,B就加固一扇门,A只能打破一半(A先手,要上取整)

#include <bits/stdc++.h>
using namespace std;

int main() {
  int n, x, y;
  cin >> n >> x >> y;
  if (x > y) return cout << n, 0;
  int cnt = 0;
  for (int i = 1; i <= n; ++i) cin >> y, cnt += y <= x;
  cout << (cnt + 1) / 2 << endl;
  return 0;
}

D.Balanced Ternary String

题意:给一个字符集是012的字符串(长度是3的倍数),可以修改某个位置上的字符,要求修改最少使得012个数相等,在此条件下输出字典序最小的串。

比较关键的想法

  • 只能把多的改成少的,否则次数不是最少

  • 0要靠前放,2要靠后放,只考虑这两个就可以(因为剩下的就是1)

#include <bits/stdc++.h>
using namespace std;
#define lop1(i, n) for (int i = 1; i <= n; ++i)
#define dlop1(i, n) for (int i = n; i >= 1; --i)
int n, cnt[3], per;
char s[300005], ans[300005];
int main() {
  cin >> n >> (s + 1);
  per = n / 3;
  lop1(i, n) 
    if (s[i] == '0' && cnt[0] < per) if (cnt[0] < per) ++cnt[0], ans[i] = '0';
  dlop1(i, n)
    if (s[i] == '2' && cnt[2] < per) if (cnt[2] < per) ++cnt[2], ans[i] = '2';
  //留下 $leq$ per 个0和2

  lop1(i, n) 
    if (!ans[i] && s[i] != '1' && cnt[0] < per) if (cnt[0] < per) ++cnt[0], ans[i] = '0';
  dlop1(i, n) 
    if (!ans[i] && s[i] != '1' && cnt[2] < per) if (cnt[2] < per) ++cnt[2], ans[i] = '2';
  //0和2哪个多就改成另外一个

  lop1(i, n) 
    if (!ans[i] && cnt[0] < per) ++cnt[0], ans[i] = '0';
  dlop1(i, n) 
    if (!ans[i] && cnt[2] < per) ++cnt[2], ans[i] = '2';
  //1改成0和2

  lop1(i, n) if (!ans[i]) ans[i] = '1';
  //剩下的是1
  lop1(i, n) putchar(ans[i]);
  return 0;
}

E.Monotonic Renumeration

题意:定义一个数组a的b数组为满足下列条件的数组

  • (b[1]=0)
  • 如果 (a_i=a_j),那么(b_i=b_j)
  • (b[i]=b[i-1]+1) 或者 (b[i]=b[i-1])

给出a,求合法的b的数量 膜998244353

计数+取模=毒瘤?好像不是这样耶...

(l[x]) 为最左边的 (x) 的位置, (r[x]) 为最右边的 (x) 的位置,由第二个条件可以发现 b数组 ([;l[x],;r[x];]) 内的数都是相等的,([;l[x],;r[x];]) 如果两个区间相交,那么它们并的区间内都是相等的

现在只剩下不相交的区间了,发现交界处有两种选择,一种是前面一段的b值+1,一种是不变(第三个条件),假设有m个不相交区间,就有m-1个交界处,答案就是 (2^{m-1})

我的代码不好看,放Tutorial里的代码

#include <bits/stdc++.h>

using namespace std;

const int MOD = 998244353;

int main()
{
 	int n;
 	scanf("%d", &n);
 	vector<int> a(n);
 	for(int i = 0; i < n; i++)
 		scanf("%d", &a[i]);
 	map<int, int> lst;
 	vector<int> last_pos(n);
 	for(int i = n - 1; i >= 0; i--)
 	{
 		if(!lst.count(a[i]))
 			lst[a[i]] = i;
 		last_pos[i] = lst[a[i]];
 	}
 	int ans = 1;
 	int cur_max = -1;
 	for(int i = 0; i < n - 1; i++)
 	{
 		cur_max = max(cur_max, last_pos[i]);
 		if(cur_max == i)
 			ans = (2 * ans) % MOD;
 	}	
 	printf("%d
", ans);
 	return 0;
}

F.Elongated Matrix

可以把每个行看成一个整体(比如看成一个点),一个点走到另一个点的代价就是 (min{a[i][k]-a[j][k]}),然后状压DP (dp[i][S]) 表示状态为S,当前走到第i个点的最大的k值,就行了。。。

发现不对,事实上还有个东西没处理,就是两列的交界处,事实上是选定的第一行跟最后一行 (min{a[i][k]-a[j][k-1]}),假设叫做 (dis1[i][j])

状压DP的过程是:先枚举起点s,算出所有状态,再枚举终点t,把答案跟 (min{dp[t][(111...)_2],dis1[s][t]}) 取max

#include <bits/stdc++.h>
using namespace std;
const int inf = 1e9;
int dis[17][19], dis1[17][19], a[17][10001], dp[17][(1 << 16) + 1], n, m, ans;

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for (int i = 0; i < n; a[i++][m] = 2 * inf)
    for (int j = 0; j < m; ++j) cin >> a[i][j];
  for (int i = 0; i < n; ++i)
    for (int j = 0; j < n; ++j) {
      int *p = a[i], *p1 = a[j];
      dis1[i][j] = dis[i][j] = inf;
      for (int k = 0; k < m; ++k) dis[i][j] = min(dis[i][j], abs(*p++ - *p1)), dis1[i][j] = min(dis1[i][j], abs(*p1++ - *p));
    }
  for (int s = 0; s < n; ++s) {// 起点
    memset(dp, 0, sizeof dp);
    dp[s][1 << s] = inf;
    for (int S = 0; S < (1 << n); ++S)
      for (int i = 0; i < n; ++i) if (S & (1 << i))
          for (int j = 0; j < n; ++j) if (!(S & (1 << j))) 
            dp[j][S | (1 << j)] = max(dp[j][S | (1 << j)], min(dp[i][S], dis[i][j]));
    for (int t = 0; t < n; ++t) ans = max(ans, min(dis1[s][t], dp[t][(1 << n) - 1]));
  }
  cout << ans;
  return 0;
}
原文地址:https://www.cnblogs.com/storz/p/10254127.html