HDU 暑假多校 2023 第八场

写在前面

补题地址:https://acm.hdu.edu.cn/listproblem.php?vol=64,题号 7361~7370。

我是一坨屎。

以下按照个人难度排序。

7367

判断每组中的点是否均在同一连通块中即可。

//
/*
By:Luckyblock
*/
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
const int kN = 2e5 + 10;
//=============================================================
int n, m;
int edgenum, head[kN], v[kN << 1], ne[kN << 1]; 
int belnum, bel[kN];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
void Add(int u_, int v_) {
  v[++ edgenum] = v_;
  ne[edgenum] = head[u_];
  head[u_] = edgenum;
}
void Dfs(int u_) {
  bel[u_] = belnum;
  for (int i = head[u_]; i; i = ne[i]) {
    int v_ = v[i];
    if (bel[v_]) continue;
    Dfs(v_);
  }
}
void Init() {
  n = read(), m = read();
  belnum = edgenum = 0;
  for (int i = 1; i <= n; ++ i) {
    head[i] = 0;
    bel[i] = 0;
  }
  for (int i = 1; i <= m; ++ i) {
    int u_ = read(), v_ = read();
    Add(u_, v_), Add(v_, u_);
  }
  for (int i = 1; i <= n; ++ i) {
    if (!bel[i]) {
      ++ belnum;
      Dfs(i);
    }
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    Init();
    int k = read(), flag = 1, nowbel = 0;
    for (int i = 1; i <= k; ++ i) {
      int a = read();
      if (nowbel == 0) nowbel = bel[a];
      else if (bel[a] != nowbel) flag = 0;
    }
    printf("%s\n", flag ? "YES" : "NO");
  }
  return 0;
}

7370

首先进行一个大力的手玩。

如果差 \(d=|b-a|\) 为完全平方数则 1 步。

否则考虑能否 2 步凑出 \(d\),即是否存在 \(d = x^2 + y^2\)\(d = x^2 - y^2\)。前者只需枚举小于 \(\sqrt{d}\) 的数检查即可,后者考虑将式子变为 \(d = (x + y)(x - y)\),仅需检查 \(d\) 的一对因数 \(p, q\) 是否满足 \(2 | p+q, 2| |p-q|\) 即可。

在上述过程中发现当 \(d\) 为奇数的时候一定满足可以满足条件并表示成 \(d = x^2 - y^2\) 的形式,于是奇数均至多需要 2 步,则偶数可以先 -1 变为奇数的情况,至多需要 3 步。

综上,先检查 \(d\) 是否为完全平方数,再检查能否 2 步凑成即可。总时间复杂度 \(O\left(T\sqrt d\right)\) 级别。

//
/*
By:Luckyblock
*/
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <iostream>
#include <algorithm>
const int kN = 1e9;
//=============================================================
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    int a = read(), b = read(), d = abs(a - b), k = sqrt(d);
    if (d == 0) printf("0\n");
    else if (k * k == d) printf("1\n");
    else {
      int flag = 0;
      for (int i = 1; i * i <= d; ++ i) {
        int dd = d - i * i, kk = sqrt(dd);
        if (kk * kk == dd) flag = 1;
        
        if (d % i == 0) {
          int j = d / i;
          if ((i + j) % 2 == 0 && (j - i) % 2 == 0) flag = 1;
        }
      }
      printf("%d\n", flag ? 2 : 3);
    }
  }
  return 0;
}

7365

考虑此时玩家 0 正在操作,两端为 1 则败,只有一端为 0 则直接取,主要问题在于如何处理两端都为 0 的情况。

发现这种情况对玩家 0 是很有利的,在他操作之后,玩家 1 只能检查能否从玩家 0 刚才取的那一端取 1,如果不能取则失败,如果取 1 之后这端又为 0,则又回到了对玩家 0 有利的情况。即如果某一端为:01010101...,两人可以不断交替地从这一端一直取。

则如果在两端为 0 的情况下,如果某端形如:010101....01 00,那么玩家 0 一直在这端取即可把玩家 1 引入必败态。如果两端均没有出现这种状态,那么可以考虑将两端的 010101... 全部删掉,得到一个玩家 0 先手操作的状态,发现这个状态的胜负态和删之前是完全相同的,再重复上述步骤进行判断即可。

模拟上述过程即可,总时间复杂度 \(O\left(\sum n\right)\) 级别。

//
/*
By:Luckyblock
*/
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
const int kN = 1e5 + 10;
//=============================================================
int n;
char s[kN];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
int Solve() {
  int l = 1, r = n, op = 0;
  while (l <= r) {
    for (; l <= r; op ^= 1) {
      if (s[l] == '0' + op && s[r] == '0' + op) break;
      if (s[l] != '0' + op && s[r] != '0' + op) return op ^ 1;
      if (s[l] == '0' + op) ++ l;
      if (s[r] == '0' + op) -- r;
    }
    if (l >= r) return -1;

    while (l < r && s[l] == '0' + op && s[l + 1] == '0' + (op ^ 1)) l += 2;
    while (l < r && s[r] == '0' + op && s[r - 1] == '0' + (op ^ 1)) r -= 2;

    if (l >= r) return -1;
    if (s[l] == '0' + op && s[l + 1] == '0' + op) return op;
    if (s[r] == '0' + op && s[r - 1] == '0' + op) return op;
  }
  return -1;
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    n = read();
    scanf("%s", s + 1); 
    printf("%d\n", Solve());
  }
  return 0;
}

7368

呃呃题,一开始吓傻了,流下了没有数理基础的泪水,后来大力手玩出的规律发做法是傻逼题。

在模 \(p\) 意义下考虑,说明对于每一个行向量 \((a_1, a_2, \dots, a_n)\),均可以通过模意义下的乘法转化为其他 \(p-2\) 个行向量;大力手玩可以发现,对于一个秩为 \(j\)\(i\times n\) 的矩阵,如果第 \(i+1\) 行加入一个新行向量后得到的 \(i+1\times n\) 矩阵秩仍为 \(j\),则新行向量的种类数为 \(p^j\)

于是考虑逐行填入行向量 DP,设 \(f_{i, j}\) 表示由前 \(i\) 行构成的 \(i\times n\) 的矩阵的秩为 \(j\) 的方案数。初始化 \(f_{0, 0} = 1\),则有:

\[f_{i, j} = f_{i - 1, j}\times p^j + f_{i - 1, j - 1} \times \left(p^n - p^{j - 1}\right) \]

答案即:

\[\mathbf{E}(\operatorname{rank}{A}) = \dfrac{1}{p^{n^2}} \sum_{i=0}^{n} i\times f_{n, i} \]

总时空复杂度均为 \(O(n^2)\) 级别。

//
/*
By:Luckyblock
*/
#include <cmath>
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define LL long long
const LL mod = 1e9 + 7;
const int kN = 5e3 + 10;
//=============================================================
int n, p;
LL cnt, facp[kN], f[kN][kN];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0'); 
  return f * w;
}
LL Qpow(LL x_, LL y_) {
  x_ %= mod;
  LL ret = 1;
  while (y_) {
    if (y_ & 1ll) ret = ret * x_ % mod;
    x_ = x_ * x_ % mod, y_ >>= 1ll;
  }
  return ret;
}
void Init() {
  n = read(), p = read();
  facp[0] = 1ll;
  for (int i = 1; i <= n; ++ i) facp[i] = 1ll * facp[i - 1] * p % mod;

  cnt = (Qpow(p, n) - 1 + mod) % mod * Qpow(p - 1, mod - 2) % mod;
}
void Solve() {
  f[0][0] = 1;
  for (int i = 1; i <= n; ++ i) {
    f[i][0] = 1ll;
    for (int j = 1; j <= i; ++ j) {
      f[i][j] = f[i - 1][j] * facp[j] % mod;
      f[i][j] = (f[i][j] + f[i - 1][j - 1] * (facp[n] - facp[j - 1] + mod) % mod) % mod;
    }
  }
}
//=============================================================
int main() {
  // freopen("1.txt", "r", stdin);
  int T = read();
  while (T --) {
    Init();
    Solve();
    LL ans = 0;
    for (int i = 1; i <= n; ++ i) {
      ans = (ans + 1ll * i * f[n][i] % mod) % mod; 
    }
    ans = ans * Qpow(Qpow(p, 1ll * n * n), mod - 2) % mod;
    printf("%lld\n", ans);
  }
  return 0;
}

写在最后

学到了什么

  • 大力手玩好啊!
  • 考虑在博弈过程中的某个状态某个人是否是占优势的。
posted @ 2023-08-12 16:39  Luckyblock  阅读(63)  评论(1)    收藏  举报