AGC035 简要题解

抵下唇峰 触碰刀锋
卸行装 归顺平庸
再异口同声唱 那告别曲
逝去的一身葱茏

A - XOR Circle

当场写了个很蛋疼的写法,总之注意到序列必须以 A , B , A ⊕ B A, B, A\oplus B A,B,AB 为循环节就基本明白了。

#include 
#include 
#include 
#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

const int N = 100010;

int n;
int a[N], b[N];

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; ++i)
    scanf("%d", &a[i]);
  if (count(a + 1, a + n + 1, 0) == n) {
    puts("Yes");
    return 0;
  }
  if (n % 3) {
    puts("No");
    return 0;
  }
  sort(a + 1, a + n + 1);
  copy(a + 1, a + n + 1, b + 1);
  int m = unique(b + 1, b + n + 1) - b - 1;
  if (m == 2 && b[1] == 0 && count(a + 1, a + n + 1, 0) == n / 3) {
    puts("Yes");
  } else if (m == 3 && (b[1] == (b[2] ^ b[3])) && count(a + 1, a + n + 1, b[1]) == n / 3 && count(a + 1, a + n + 1, b[2]) == n / 3 && count(a + 1, a + n + 1, b[3]) == n / 3)
    puts("Yes");
  else
    puts("No");
  return 0;
}

B - Even Degrees

任选一个生成树做主元。

#include 
#include 
#include 
#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

struct E {
  int v;
  bool vis;
  E *next, *rev;
};

const int N = 100010;

int n, m;
bool vis[N], par[N];
E* g[N];

E* adde(int u, int v) {
  static E pool[N * 2], *p = pool;
  ++p;
  p->v = v;
  p->next = g[u];
  g[u] = p;
  return p;
}

void dfs(int u) {
  vis[u] = true;
  for (E* p = g[u]; p; p = p->next)
    if (!p->vis) {
      p->vis = p->rev->vis = true;
      if (!vis[p->v]) {
        dfs(p->v);
        if (par[p->v]) {
          printf("%d %d\n", p->v, u);
        } else {
          printf("%d %d\n", u, p->v);
          par[u] ^= 1;
        }
      } else {
        printf("%d %d\n", u, p->v);
        par[u] ^= 1;
      }
    }
}

int main() {
  scanf("%d%d", &n, &m);
  if (m % 2) {
    puts("-1");
    return 0;
  }
  while (m--) {
    int u, v;
    scanf("%d%d", &u, &v);
    E *p = adde(u, v), *q = adde(v, u);
    p->rev = q;
    q->rev = p;
  }
  dfs(1);
  return 0;
}

C - Skolem XOR Tree

构造方法见代码。

#include 
#include 
#include 
#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

int main() {
  int n;
  scanf("%d", &n);
  if (!(n & (n - 1))) {
    puts("No");
    return 0;
  }
  puts("Yes");
  int m = n;
  printf("%d %d\n", 1 + n, 3);
  if (n % 2 == 0) --m;
  for (int i = 2; i < m; i += 2) {
    printf("%d %d\n", i, i + 1);
    printf("%d %d\n", i, 1);
    printf("%d %d\n", i + n, i + 1 + n);
    printf("%d %d\n", i + n + 1, 1);
  }
  if (n % 2 == 0) {
    int x = n & -n;
    int y = n - x;
    printf("%d %d\n", x, n);
    printf("%d %d\n", y + n + 1, n * 2);
  }
  return 0;
}

D - Add and Remove

考虑把过程反过来,考虑计算最后移除的那个数的贡献。可以状压 DP。稍微分析一下可以发现可以 Θ ( 2 n ) \Theta(2^n) Θ(2n) 表示,此题的数据范围不需要实现得那么精细。

#include 
#include 
#include 
#include 
#include 
#include 
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());
 
template <class T>
istream& operator>>(istream& is, vector<T>& v) {
  for (T& x : v)
    is >> x;
  return is;
}
 
template <class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
  if (!v.empty()) {
    os << v.front();
    for (int i = 1; i < v.size(); ++i)
      os << ' ' << v[i];
  }
  return os;
}

const int N = 20;

int n;
int a[N];
map<pair<int, int>, ll> dp[N][N];

ll dfs(int l, int r, int x, int y) {
  if (l + 1 == r) return 0;
  auto it = dp[l][r].find(make_pair(x, y));
  if (it != dp[l][r].end())
    return it->second;
  ll cur = 1LL << 60;
  for (int i = l + 1; i < r; ++i)
    cur = min(cur, dfs(l, i, x, x + y) + dfs(i, r, x + y, y) + a[i] * (ll)(x + y));
  return dp[l][r][make_pair(x, y)] = cur;
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  cin >> n;
  for (int i = 1; i <= n; ++i)
    cin >> a[i];
  cout << (dfs(1, n, 1, 1) + a[1] + a[n]) << '\n';

  return 0;
}

E - Develop

对于 k k k 是偶数的情况分别 DP 即可。剩下的情况可以发现对于任何一个 d ≥ 1 d\ge 1 d1,如果有一个长为 2 d + 1 2d + 1 2d+1 的连续段都被扣掉了,那么两边再各一个有一个被扣掉,对称部分总共不能达到 k k k,对这个前面的连续段向后造成的约束进行 DP 可以发现奇偶部分最多一个需要限制。

#include 

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template <class T>
istream& operator>>(istream& is, vector<T>& v) {
  for (T& x : v)
    is >> x;
  return is;
}

ostream& operator<<(ostream& os, const pair<char, int>& unit) {
  return os << unit.first << "^" << unit.second;
}

template <class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
  if (!v.empty()) {
    os << v.front();
    for (int i = 1; i < v.size(); ++i)
      os << ' ' << v[i];
  }
  return os;
}

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  int n, k, M;
  cin >> n >> k >> M;
  function<int(int)> norm = [&](int x) { return x >= M ? x - M : x; };
  function<void(int&, int)> add = [&](int& x, int y) {
    (x += y) >= M ? x -= M : 0;
  };
  function<void(int&, int)> sub = [&](int& x, int y) {
    (x -= y) < 0 ? x += M : 0;
  };
  if (k % 2 == 0) {
    function<vi(int, int)> easy = [&](int n, int k) {
      vi dp(n + 2);
      dp[0] = 1;
      for (int i = 1; i <= n + 1; ++i)
        for (int j = 1; j <= k + 1 && j <= i; ++j)
          add(dp[i], dp[i - j]);
      dp.erase(dp.begin());
      return dp;
    };
    vi need = easy((n + 1) / 2, k / 2);
    cout << need[n / 2] * (ll)need[(n + 1) / 2] % M;
  } else {
    int nn = n / 2 + 1;
    static int dp[80][80][160], newdp[80][80][160];
    dp[0][0][0] = 1;
    const int INF = 1 << 30;
    static int glt[80][80];
    function<int(int, int)> gl = [&](int a, int b) {
      a = min(a, k / 2 + 2);
      if ((b - a + 1) * 2 < k - a * 2 + 3 || a <= 0)
        return INF;
      return (k - a * 2 + 3) / 2 - 1;
    };
    for (int i = 0; i <= nn; ++i)
      for (int j = 0; j <= nn; ++j) {
        if (i >= k / 2 + 2 && j >= k / 2 + 1) continue;
        glt[j + 1][i] = gl(j + 1, i);
      }
    for (int rep = 0; rep < n; ++rep) {
      memset(newdp, 0, sizeof(newdp));
      for (int i = 0; i <= nn; ++i)
        for (int j = 0; j <= nn; ++j) {
          if (i >= k / 2 + 2 && j >= k / 2 + 1) continue;
          add(newdp[0][i][0], dp[i][j][0]);
          int v = glt[j + 1][i];
          if (j < k / 2 + 1 || i < k / 2 + 1)
            add(newdp[j + 1][i][v == INF ? 0 : ((v + 1) << 1)], dp[i][j][0]);
          add(newdp[0][i][0], dp[i][j][2]);
          for (int l = 1; l <= nn; ++l) {
            add(newdp[0][i][0], dp[i][j][(l + 1) << 1]);
            if (j < k / 2 + 1 || i < k / 2 + 1)
              add(newdp[j + 1][i][(l - 1) << 1 | 1], dp[i][j][(l + 1) << 1]);
          }
          for (int l = 0; l <= nn; ++l) {
            add(newdp[0][i][(l + 1) << 1], dp[i][j][l << 1 | 1]);
            if (j < k / 2 + 1 || i < k / 2 + 1) {
              add(newdp[j + 1][i][(min(glt[j + 1][i], l) + 1) << 1], dp[i][j][l << 1 | 1]);
            }
          }
        }
      memcpy(dp, newdp, sizeof(dp));
    }
    int ans = 0;
    for (int i = 0; i <= nn; ++i)
      for (int j = 0; j <= nn; ++j)
        for (int l = 0; l <= n + 5; ++l)
          add(ans, dp[i][j][l]);
    cout << ans;
  }

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

F - Two Histograms

考虑对每个 hist 枚举情况,发现重复的必然是一个点被本行和本列的 hist 抢夺,我们强制规定一个竖直的 hist 顶到的位置下方不能恰好是横向的 hist 顶到的。这样反而考虑每个横着的 hist 有几个位置被约束,易列生成函数

N ! [ x N ] ( ( N + 1 ) + ( N + 1 − 1 ) x + N + 1 − 2 2 ! x 2 + ⋯ + x N N ! ) M ( 1 + x + x 2 2 ! + ⋯ + x N N ) N![x^N]((N + 1) + (N + 1 - 1)x + \frac{N + 1 - 2}{2!}x^2 + \cdots + \frac{x^N}{N!})^M (1 + x + \frac{x^2}{2!} + \cdots + \frac{x^N}{N}) N![xN]((N+1)+(N+11)x+2!N+12x2++N!xN)M(1+x+2!x2++NxN)

易见把每个多项式拓展为无限形式更简单,这个分明就是

N ! [ x N ] ( ( N + 1 ) − x ) M e ( M + 1 ) x N![x^N] ((N + 1) - x)^M \mathrm{e}^{(M+1)x} N![xN]((N+1)x)Me(M+1)x

于是预处理阶乘和幂即可计算。

#include 

#define LOG(FMT...) fprintf(stderr, FMT)

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef vector<int> vi;

// mt19937 rng(chrono::steady_clock::now().time_since_epoch().count());

template <class T>
istream& operator>>(istream& is, vector<T>& v) {
  for (T& x : v)
    is >> x;
  return is;
}

ostream& operator<<(ostream& os, const pair<char, int>& unit) {
  return os << unit.first << "^" << unit.second;
}

template <class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
  if (!v.empty()) {
    os << v.front();
    for (int i = 1; i < v.size(); ++i)
      os << ' ' << v[i];
  }
  return os;
}

const int N = 500010, P = 998244353;

int n, m;
int inv[N], ifac[N], fac[N], pw[N], qw[N];

int main() {
#ifdef LBT
  freopen("test.in", "r", stdin);
  int nol_cl = clock();
#endif
  ios::sync_with_stdio(false);
  cin.tie(nullptr);

  cin >> n >> m;
  if (n < m) swap(n, m);
  inv[1] = 1;
  for (int i = 2; i <= n; ++i)
    inv[i] = -(P / i) * (ll)inv[P % i] % P + P;
  ifac[0] = 1;
  for (int i = 1; i <= n; ++i)
    ifac[i] = ifac[i - 1] * (ll)inv[i] % P;
  fac[0] = 1;
  for (int i = 1; i <= n; ++i)
    fac[i] = fac[i - 1] * (ll)i % P;
  pw[0] = 1;
  for (int i = 1; i <= m; ++i)
    pw[i] = pw[i - 1] * (n + 1LL) % P;
  qw[0] = 1;
  for (int i = 1; i <= n; ++i)
    qw[i] = qw[i - 1] * (m + 1LL) % P;
  int ans = 0;
  for (int i = 0; i <= m; ++i) {
    int res = ((i & 1) ? (P - 1) : 1) * (ll)pw[m - i] % P * ifac[i] % P * ifac[m - i] % P * qw[n - i] % P * ifac[n - i] % P;
    ans = (ans + res) % P;
  }
  ans = ans * (ll)fac[n] % P * fac[m] % P;
  cout << ans;

#ifdef LBT
  LOG("Time: %dms\n", int ((clock()
      -nol_cl) / (double)CLOCKS_PER_SEC * 1000));
#endif
  return 0;
}

你可能感兴趣的:(题集/比赛题解)