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\),则有:
答案即:
总时空复杂度均为 \(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;
}
写在最后
学到了什么
- 大力手玩好啊!
- 考虑在博弈过程中的某个状态某个人是否是占优势的。

浙公网安备 33010602011771号