写在前沿:这场小白赛难度不大,但是模拟题太多太多了,码量巨大,并不是很喜欢。
因为模拟太多赛后进行代码改良许多参考了码量很少的hx073269的代码。
A、拼三角
组给出
个数,询问能不能构成两个三角形。
三种做法都是暴力,不可以通过去比较更小的三个和更大的三个,这里给出出题人的例子。
3 8 12 15 16 16 3 15 16一组 8 12 16一组
暴力就容易了,可以写,也可以写二进制枚举,也可以写
。我给我写的二进制枚举的做法吧。
#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
#define repp(i, sta, en) for(int i=sta; i>=en; --i)
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar()) s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op) putchar(op); return; } char F[40]; ll tmp = x > 0 ? x : -x; if (x < 0)putchar('-'); int cnt = 0; while (tmp > 0) { F[cnt++] = tmp % 10 + '0'; tmp /= 10; } while (cnt > 0)putchar(F[--cnt]); if (op) putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1; while (b) { if (b & 1) ans *= a; b >>= 1; a *= a; } return ans; } ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
struct Node {
ll val;
int id;
bool operator < (const Node& opt) const {
return val < opt.val;
}
};
const int N = 1e6 + 7;
ll n, m;
ll p[10];
bool calc(int i, int j, int k) {
return p[i] + p[j] > p[k];
}
void solve() {
rep(i, 0, 5) p[i] = read();
sort(p, p + 6);
m = (1 << 6) - 1;
rep(i, 0, m) { // 1代表在第一个集合,0代表在第二个集合
if (__builtin_popcount(i) != 3) continue;
vector<int> a, b;
rep(j, 0, 5) {
if (i >> j & 1) a.push_back(j);
else b.push_back(j);
}
if (calc(a[0], a[1], a[2]) and calc(b[0], b[1], b[2])) {
puts("Yes");
return;
}
}
puts("No");
}
int main() {
int T = read(); while (T--)
solve();
return 0;
} B、相对分子质量
给出你每个原子的相对原子质量,叫你求给出的分子的相对分子质量。
因为答案保证在范围内,所以还好不用开高精度,那么这种题目就是开栈去模拟了,因为给出的分子式一定合法,所以最后栈顶的元素就是答案了。
但是注意,它这里给出的原子可能会有个字符的,所以有两种处理办法,一种是把
个字符的也看着字符串,下面去
拿到哪个字符串去比较,还有一种就是我的做法,新开了一个
表,统计这种有
个字符的原子,因为它们都有一个特点就是第二个字符一定是小写的,依据这个判断就可成立。
const int N = 1e5 + 7;
string s;
stack<ll> st;
unordered_map<char, int> mp;
unordered_map<string, int> mp2;
int main() {
js;
string ch;
int num, n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> ch >> num;
if (ch.size() == 1)
mp[ch[0]] = num;
else
mp2[ch] = num;
}
while (m--) {
while (st.size()) st.pop();
cin >> s;
s = "#(" + s + ")#";
st.push(0);
int n = s.size();
for (int i = 0; i < n; ++i) {
if (s[i] == '(') st.push(0);
else if (s[i] == ')') {
ll m = st.top(), base = 0; st.pop();
while (i < n and isdigit(s[i + 1])) {
base = 10 * base + (s[i + 1] - '0');
++i;
}
st.top() += m;
if (base) st.top() += m * (base - 1);
}
else if (isalpha(s[i])) {
if (s[i + 1] >= 'a' and s[i + 1] <= 'z') {
st.top() += mp2[s.substr(i, 2)];
++i;
}
else
st.top() += mp[s[i]];
}
else if (isdigit(s[i])) {
if (s[i - 1] >= 'A' and s[i - 1] <= 'Z') {
char ch = s[i - 1];
ll base = 0;
while (i < n and isdigit(s[i])) {
base = 10 * base + (s[i] - '0');
++i;
}
--i;
st.top() += (base - 1) * mp[ch];
}
else {
ll base = 0;
while (i < n and isdigit(s[i])) {
base = 10 * base + (s[i] - '0');
++i;
}
--i;
st.top() += (base - 1) * mp2[s.substr(i - 2, 2)];
}
}
}
cout << st.top() << endl;
}
return 0;
} C、消除整数
发现我们可以减的数是,也就是
,很容易想到转换二进制去考虑。
那么对于每次操作,我们只能把减数变大,并且很显然的结论对于每个减数我们一定只会减次或者
次,所以我们把
写出二进制形式,对于它二进制中每一个
我们只需要减掉一次即可,对于它二进制中的每一个
我们要减
次,如果只减一次,下一次把减数变成两倍,那么这个位置就变成
并且永远无法消除。
const int N = 1e6 + 7;
int n, m;
ll p[N];
void solve() {
n = read();
int cnt = 0;
m = 1;
while (n) {
if (n & m) {
++cnt;
n -= m;
}
else {
cnt += 2;
n -= 2 * m;
}
m <<= 1;
}
print(cnt);
} D、消灭星星
给出的地图,星号代表敌人,里面存在
种大写字母,每次你做消灭敌人操作都会消灭某一行或者某一列,相对的字母也会被你消灭,问有没有一种方法使得至少存在
种字母完全不受影响情况下消灭全部星星。完全不受影响就是这种字母一个都没少的留在了地图上。
根据数据范围我们发现地图中的总字母数都较少,所以我们可以二进制的枚举全部留下的字符,再去遍历地图转化为判定这种方法是不是存在。时间复杂度。
const int N = 20 + 7;
ll n, m, k, t;
char s[N][N];
bool x[N], y[N], vis[1 << 11];
bool check(int bit) {
ms(x, 0); ms(y, 0);
rep(i, 0, m) {
if (bit >> i & 1) {
if (!vis[i]) return 0;
rep(j, 0, n - 1)
rep(k, 0, n - 1)
if (s[j][k] == 'A' + i) x[j] = 1, y[k] = 1;
}
}
rep(i, 0, n - 1) {
rep(j, 0, n - 1) {
if (s[i][j] == '*' and x[i] and y[j])
return 0;
}
}
return 1;
}
void solve() {
ms(vis, 0);
n = read(), m = read(), k = read();
rep(i, 0, n - 1) {
scanf("%s", s[i]);
rep(j, 0, n - 1) {
if (isalpha(s[i][j]))
vis[s[i][j] - 'A'] = 1;
}
}
t = (1 << m) - 1;
rep(i, 0, t) {
if (__builtin_popcount(i) < m - k) continue; // 统计i的二进制表示中1的个数
if (check(i)) {
puts("yes");
return;
}
}
puts("no");
} E、春游
你带领着一共个人,你要把他们全部安排去划船,你可以花费
安排一个双人船,你也可以花费
安排一个三人船。问安排全部人都有船坐的最小花费是多少?
首先我们可以计算双人船和三人船中每个玩家的花费,我们要在尽可能的情况下选择单价少的船优先安排。
如果,说明我们要先尽可能安排双人船坐满,如果恰好
,那就不需要额外安排船,如果剩下一个人,那么就考虑是给他新添一艘船或者拿掉一个
,和之前
个人一起去坐
人船。
如果,说明我们要先尽可能安排三人船坐满,那么与上分类似,我们可能留下
三种情况,方便做一次取最值操作即可。
ll n, a, b;
void solve() {
n = read(); a = read(), b = read();
if (n <= 2) {
print(min(a, b));
return;
}
ll val1 = a / 2, val2 = b / 3;
ll ans;
if (val1 < val2) {
ans = (n / 2) * a;
if (n & 1) {
ans = min({ ans + min(a,b),ans - a + b });
}
}
else {
ans = (n / 3) * b;
if (n % 3 == 1) {
ans = min({ ans + min(a,b),ans - b + 2 * a });
}
else if (n % 3 == 2) {
ans = min({ ans + min(a,b),ans - b + 3 * a });
}
}
print(ans);
} F、五连珠
模拟题,看谁代码写的简单漂亮了,考虑数组传参写的简单一些。
const int N = 6;
struct Node {
ll val;
int id;
bool operator < (const Node& opt) const {
return val < opt.val;
}
};
ll n, m;
int a[N][N], b[N][N];
bool check(int c[N][N]) {
int cnt = 0;
rep(i, 1, 5) {
cnt = 0;
rep(j, 1, 5) cnt += c[i][j]; // 行
if (!cnt) return 1;
cnt = 0;
rep(j, 1, 5) cnt += c[j][i]; // 列
if (!cnt) return 1;
}
cnt = 0;
rep(i, 1, 5) cnt += c[i][i]; // 主对角线
if (!cnt) return 1;
cnt = 0;
rep(i, 1, 5) cnt += c[5 - i + 1][i]; // 副对角线
return cnt == 0;
}
void solve() {
rep(i, 1, 5)
rep(j, 1, 5) a[i][j] = read();
rep(i, 1, 5)
rep(j, 1, 5) b[i][j] = read();
int ans = INF;
rep(_, 1, 25) {
int x = read();
if (ans != INF) continue;
rep(i, 1, 5)
rep(j, 1, 5)
if (a[i][j] == x) a[i][j] = 0;
rep(i, 1, 5)
rep(j, 1, 5)
if (b[i][j] == x) b[i][j] = 0;
bool flaga = check(a), flagb = check(b);
if (flaga and flagb) ans = 0;
else if (flaga) ans = 1;
else if (flagb) ans = 2;
}
print(ans);
} G、有始有终
给出的地图,每个格点有一定的高度,你只可以再高度不大于
之间的格点自由移动,如果你要跨越一个高度大于
的相邻格点那么就要花费一次技能,把一个格点变成电梯这样就可以忽略它和其他全部相邻点的高度差,问从起始点去到终点最少的技能使用次数。
广搜最短路的题目,如果我们把全部可以不用技能走到的点边权看成,走不过去的点边权看作
,那么就变成了从起点到终点的最短路了,第一步我们首先得到从起点出发不使用一次技能可以去到哪些点,记录下这些点花费都是
,接下来,我们从这些点继续出发去到我们不能去到的点,并把这些点花费记成
,这里注意我们选择变成电梯的点一定是我们下一步要走的点,这样我们就可以上去并且再下一步的选择更多,接下来再去从这些变成电梯的点继续
往下合并点,直到合并到了终点。
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int N = 30 + 7;
pai be, ed;
#define x first
#define y second
int n, m, d;
int a[N][N];
int dis[N][N], tot;
queue<pai> q;
pai b[N * N];
void dfs(pai now, bool limit) {
rep(i, 0, 3) {
int dx = now.x + dir[i][0], dy = now.y + dir[i][1];
if (dx<1 or dx>n or dy<1 or dy>m) continue;
if (dis[dx][dy] != -1) continue;
if (limit and abs(a[dx][dy] - a[now.x][now.y]) > d) continue;
dis[dx][dy] = dis[now.x][now.y];
q.push({ dx,dy });
dfs({ dx,dy }, 1);
}
}
void solve() {
ms(dis, -1);
while (q.size()) q.pop();
m = read(), n = read();
be.y = read(), be.x = read();
ed.y = read(), ed.x = read();
d = read();
rep(i, 1, n)
rep(j, 1, m) a[i][j] = read();
dis[be.x][be.y] = 0;
q.push(be);
dfs(be, 1); // 把不用技能可以去到的点都走一遍
while (dis[ed.x][ed.y] == -1 and q.size()) {
tot = 0;
while (q.size()) {
b[++tot] = q.front(); q.pop();
}
rep(i, 1, tot) {
rep(j, 0, 3) {
int dx = b[i].x + dir[j][0], dy = b[i].y + dir[j][1];
if (dx<1 or dx>n or dy<1 or dy>m) continue;
if (dis[dx][dy] == -1) {
q.push({ dx,dy });
dis[dx][dy] = dis[b[i].x][b[i].y] + 1;
}
}
}
tot = 0;
while (q.size()) {
b[++tot] = q.front(); q.pop();
}
rep(i, 1, tot) dfs(b[i], 0);
}
print(dis[ed.x][ed.y]);
} H、匹配矩阵
再次模拟。。直接上代码了吧,仔细一点就可以了。
const int N = 20 + 7;
struct Node {
ll val;
int id;
bool operator < (const Node& opt) const {
return val < opt.val;
}
};
ll n, m;
char a[N][N], b[4][N][N];
int n1, n2, m1, m2;
int calc(char s[N][N], int n2, int m2) {
int res = 0;
rep(i, 1, n1 - n2 + 1) {
rep(j, 1, m1 - m2 + 1) {
bool flag = 1;
for (int x = 1; flag and x <= n2; ++x)
for (int y = 1; flag and y <= m2; ++y)
if (s[x][y] != '#' and s[x][y] != a[i + x - 1][j + y - 1])
flag = 0;
res += flag;
}
}
return res;
}
void solve() {
n1 = read(), m1 = read();
rep(i, 1, n1) scanf("%s", a[i] + 1);
n2 = read(), m2 = read();
rep(i, 1, n2) scanf("%s", b[0][i] + 1);
rep(i, 1, n2)
rep(j, 1, m2) b[1][j][n2 - i + 1] = b[0][i][j];
rep(i, 1, n2)
rep(j, 1, m2) b[2][n2 - i + 1][m2 - j + 1] = b[0][i][j];
rep(i, 1, n2)
rep(j, 1, m2) b[3][m2 - j + 1][i] = b[0][i][j];
int ans = calc(b[0], n2, m2); // 0度
ans += calc(b[1], m2, n2); // 90度
ans += calc(b[2], n2, m2); // 180度
ans += calc(b[3], m2, n2); // 270度
print(ans);
} I、螺旋矩阵
题目意思比较简单,实现4个方向的转移可以比较取巧,发现我们在走到端点才要换方向,那么端点都有一个共同的特性,那就是一定有个点没被填写过,起点比较特殊他有
个点没被写过,但是不妨碍我们换方向。
下一步我们要往数组左边填数,我们考虑进行坐标平移,考虑一下数据范围的字符串最大也就是
,所以我们选择
做为我们新的起点依次往下填数即可。
const int dir[][2] = { {0,1},{-1,0},{0,-1},{1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
ll n, m;
char s[5007];
char ans[200][200];
void solve() {
ms(ans, -1);
scanf("%s", s);
n = strlen(s);
int maxx = 100, minx = 100, maxy = 100, miny = 100;
int op = 3;
int x = 100, y = 100;
rep(i, 0, n - 1) {
ans[x][y] = s[i];
maxx = max(maxx, x); maxy = max(maxy, y);
minx = min(minx, x); miny = min(miny, y);
int cnt = 0;
rep(j, 0, 3) {
int dx = x + dir[j][0], dy = y + dir[j][1];
if (ans[dx][dy] == -1) ++cnt;
}
if (cnt >= 3) op = (op + 1) % 4;
x += dir[op][0];
y += dir[op][1];
}
char p[207];
rep(i, minx, maxx) {
rep(j, miny, maxy)
if (ans[i][j] == -1) p[j - miny] = ' ';
else p[j - miny] = ans[i][j];
p[maxy - miny + 1] = '\0';
puts(p);
}
}
int main() {
int T = read(); while (T--) {
solve();
if (T) puts("");
}
return 0;
} J、统计个数
做法1、直接暴力,枚举全部中间点,依次求解数量即可,因为我们最终要把三角形数量乘,通过转化发现并不需要
以外其他操作,时间复杂度
做法参考hc唐宇群。
const int N = 200 + 7;
ll n, m;
bool mp[N][N];
int tri = 0, line = 0;
void dfs(int k) {
rep(i, 1, n) {
if (mp[i][k]) {
rep(j, 1, n) {
if (k == j) continue;
if (mp[i][j]) {
++line; // 一条线
if (mp[k][j]) ++tri; // 一个三角
}
}
}
}
}
void solve() {
ms(mp, 0);
n = read(), m = read();
rep(i, 1, m) {
int u = read(), v = read();
mp[u][v] = mp[v][u] = 1;
}
tri = line = 0;
rep(i, 1, n) dfs(i);
if (!tri) puts("0/1");
else {
/*
line /= 2;
tri /= 6;
tri *= 3;
*/
int tmp = gcd(tri, line);
tri /= tmp, line /= tmp;
printf("%d/%d\n", tri, line);
}
} 做法2、优化边权关系,我们发现我们要用
去判断每每点之间是不是有关系,这时候就要想起我们卡常大师
,用它去优化我们的边权关系,可以在加快查询速度,听说可以把
出到
。
const int N = 200 + 7;
ll n, m;
bool mp[N][N];
int du[N];
bitset<N> b[N], p[N]; // b代表图中关系,p保证选取的三角形(a,b,c) a<b<c
void solve() {
ms(mp, 0); ms(du, 0);
n = read(), m = read();
rep(i, 1, n) b[i].reset(); // 清空
rep(i, 1, m) {
int u = read(), v = read();
mp[u][v] = mp[v][u] = 1;
++du[u], ++du[v];
b[u][v] = b[v][u] = 1;
}
int tri = 0, line = 0;
rep(i, 1, n) line += du[i] * (du[i] - 1) / 2; // 固定中点,出度中选择2个不同的点
rep(i, 1, n) {
rep(j, i + 1, n) {
if (mp[i][j])
tri += (b[i] & b[j] & p[j]).count();
}
}
if (!tri) {
puts("0/1");
}
else {
tri *= 3;
int tmp = gcd(tri, line);
tri /= tmp, line /= tmp;
printf("%d/%d\n", tri, line);
}
}
int main() {
rep(i, 1, N - 1)
rep(j, i + 1, N - 1)
p[i][j] = 1;
int T = read(); while (T--)
solve();
return 0;
} 
京公网安备 11010502036488号