【算法竞赛】CF #816 (Div. 2) A-D Rethink

2022-10-26 16:21:31 浏览数 (2)

闲言

本人赛时,跳过了C,感觉D更有思路一点,然后也没乱搞出来D

事实证明,又是比赛脑子不好使的表现。

将贴出代码会在文章末尾贴出。

A

  1. 必然是让左上的直接走到右下,让左下的直接走到右上。
  2. 因为一次滑行就要一格能量,所以,一次尽量长,所以直接一条通到底是最好的(如题中样例解释图例)
  3. 之后便可以尝试,发现除了(1,1)需要特判,其他结果都是 (m - 1) * 2 n

B

因为是下取整,所以,如果从sum里分出的[x/k]结果大于0的话,其实是相当于没分出去。

所以,为了减小beauty值,只能每次分出去k-1到其他一格中。

不难发现sum为s的数组,它的beauty的最大值和最小值分别是 s / k(s - cnt * (k - 1)) / k

这样就可以判断无解的情况。

然后,具体方案,就可以通过模拟上述过程实现。

C

发现如果所有数都一样,题目中所定义的值为n*(n 1)/2

那后面就是修改的事情了(因为一开始的数据也可以看作是修改来的)

我们可以发现,只有当修改的数据对整体的所定义值的影响,只和 与旁边的数的关系它的位置有关

  1. 若和旁边数的关系是 相等,它就对整体没有除 基础影响(所有数都一样时对整体的值的贡献)外的影响了;
  2. 若和旁边的数的关系是 相异,它对整体的影响就是,包含这两个数组成的区间的大区间的总数,即 大区间的左边界可取值的数的数量乘以 大区间的左边界可取值的数的数量,即 i*(n-i)(假定两数的位置关系为(i,i 1))

D

我从这位大佬的题解中明白的

题中要求最小字典序,即让在前面的数越小越好。

首先,容易理解,每个数的每个二进制位之间是相互独立互不影响的。

所以,我们需要通过 或的结果得出每个数需要满足条件的最大的数,然后再删去 1,把字典序变小。

一个数可能和多个数有关系,那么把这些 或的结果进行 与运算,我们就能够得到,那个数能满足答案的最大值。因为这些 或的结果中有某位是0,那么这个数的这一位就必不能是 1

因此,我们让它尽量取 1了。所以得到的是那个数能满足答案的最大值。

这样之后,为了使得字典序最小,我们要从最前面的数开始把某一位 1改成 0,同时check这样改对和它相关联的数是否就算它取最大值也不能成立(这个数这一位取 0,与它相关的某个数,这一位因为和其他的数有关,不能取 1,那就无法维持原来的或的结果,即不行)

这里关于为什么要按位来操作,我其实没什么想法,因为我赛时就是,直接两个数进行位运算,然后没有过。

但,仔细想想是能发现,如果粗暴的用一个数单独的满足两个数的关系(就是一个数都用另一个数对应的位数来改)会让那个数大大受限,从而使得可能到达不了 最终的答案

CODE

D

代码语言:javascript复制
#pragma GCC optimize(2)

#include <bits/stdc  .h>
#define all(x) (x).begin(), (x).end()

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1e5   10;

int n, m;
int a[N];
vector<PII> s[N];
bool flag[N];

int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0);
  // freopen("i.txt", "r", stdin);
  // freopen("o.txt", "w", stdout);
  cin >> n >> m;
  while (m--)
  {
    int i, j, x;
    cin >> i >> j >> x;
    s[j].push_back({i, x});
    s[i].push_back({j, x});
  }
  for (int i = 1; i <= n; i  )
  {
    if (s[i].size())
    {
      a[i] = (1 << 30) - 1;
      for (auto x : s[i])
        a[i] &= x.second;
    }
  }
  for (int i = 1; i <= n; i  )
    for (int j = 0; j < 30; j  )
      if (a[i] >> j & 1)
      {
        bool flag = false;
        for (auto x : s[i])
          if (!(a[x.first] >> j & 1) || x.first == i)
          {
            flag = true;
            break;
          }
        if (!flag)
          a[i] -= (1 << j);
      }
  for (int i = 1; i <= n; i  )
    cout << a[i] << " n"[i == n];
  return 0;
}
/*
没有想着一位一位处理
*/

0 人点赞