和漂亮大姐姐的 ACM 日常 | ucup 题解报告
AtomAlpaca · · 个人记录
队友是漂亮姐姐 qixi,两人队。
现已因理念不合解散。
自己过的题目会放代码。
The 3rd Universal Cup. Stage 24: Poland
难度 ★★★,成绩 7/12(ABGHIJK)
A. Acronym
直接暴力枚举每个单词即可。
B. Baggage
不妨令
注意要在跑最短路时候取,因为如果你跑完最短路再取会影响其他一些路径在取形式如
G. Game MPO
发现
#include <bits/stdc++.h>
#include <cstdio>
typedef long long ll;
const int MAX = 1e3 + 5;
const int dx[15] = {0, -1, 0, 1, -1, 1, -1, 0, 1};
const int dy[15] = {0, -1, -1, -1, 0, 0, 1, 1, 1};
char s[MAX][MAX];
ll n, ans, _ans;
bool f(char c) { return c == 'M' or c == 'O' or c == 'P'; }
bool g(char c) { return c == 'm' or c == 'o' or c == 'p'; }
int P(int x, int y)
{
int res = 0;
for (int i = 1; i <= 8; ++i) { res += s[x + dx[i]][y + dy[i]] == 'M'; }
return res;
}
int M(int x, int y)
{
int res = 0;
for (int i = 1; i <= 8; ++i) { res += s[x + dx[i]][y + dy[i]] == 'P'; }
return res;
}
int O(int x, int y)
{
int res = 0;
for (int i = 1; i <= 8; ++i) { res += s[x + dx[i]][y + dy[i]] == 'O'; }
return res;
}
bool check(int x, int y)
{
if (s[x][y] == 'p') { return P(x, y); }
if (s[x][y] == 'm') { return M(x, y); }
if (s[x][y] == 'o') { return O(x, y); }
return false;
}
int main()
{
scanf("%lld", &n);
for (int i = 1; i <= n; ++i) { scanf("%s", s[i] + 1); }
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
if (f(s[i][j])) { ++ans; }
if (s[i][j] == 'P') { _ans += P(i, j); }
if (s[i][j] == 'M') { _ans += M(i, j); }
if (s[i][j] == 'O') { _ans += O(i, j); }
}
}
ans += _ans / 2;
printf("%lld ", ans);
while (true)
{
bool flag = false;
for (int i = 1; i <= n; ++i)
{
for (int j = 1; j <= n; ++j)
{
if (check(i, j))
{
if (s[i][j] == 'o') { s[i][j] = 'O'; ans += 1 + O(i, j); }
if (s[i][j] == 'p') { s[i][j] = 'P'; ans += 1 + P(i, j); }
if (s[i][j] == 'm') { s[i][j] = 'M'; ans += 1 + M(i, j); }
flag = true;
goto pos;
}
}
}
pos:
if (!flag) { break; }
}
printf("%lld\n", ans);
for (int i = 1; i <= n; ++i)
{
for(int j = 1; j <= n; ++j) { printf("%c", s[i][j]); }
printf("\n");
}
}
H
考虑要挑战的一列高度
考虑从后往前 dp,
I. Imbalanced Teams
最优解一定是一个队取最大的
令
注意两队完全一样的时候要除以二。
#include <bits/stdc++.h>
#include <cstdio>
typedef long long ll;
const int MAX = 20005;
const int MAXX = 1e6 + 5;
const int MOD = 1e9 + 7;
ll n, k, ans, a[MAX], b[MAXX], c[MAXX], d[MAXX], dif;
ll f[MAXX], g[MAXX];
ll qp(ll a, ll x)
{
ll res = 1;
while (x) { if (x & 1) { res = res * a % MOD; } x >>= 1; a = a * a % MOD; }
return res;
}
ll inv(ll x) { return qp(x, MOD - 2); }
ll C(ll x, ll y) { return f[x] * g[y] % MOD * g[x - y] % MOD; }
void init(ll x)
{
g[0] = f[0] = 1;
for (int i = 1; i <= x; ++i) { f[i] = f[i - 1] * i % MOD; } g[x] = qp(f[x], MOD - 2);
for (int i = x - 1; i >= 1; --i) { g[i] = g[i + 1] * (i + 1) % MOD; }
}
int main()
{
scanf("%lld%lld", &n, &k); ans = 1;
init(MAXX - 5);
for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); ++b[a[i]]; }
std::sort(a + 1, a + n + 1);
for (int i = 1; i <= k; ++i)
{
++c[a[i]]; ++d[a[n - i + 1]];
dif += a[n - i + 1] - a[i];
}
for (int i = 1; i <= n; ++i)
{
if (c[a[i]] or d[a[i]])
{
ans = ans * C(b[a[i]], c[a[i]] + d[a[i]]) % MOD;
ans = ans * C(c[a[i]] + d[a[i]], c[a[i]]) % MOD;
c[a[i]] = d[a[i]] = 0;
}
}
if (a[1] == a[n]) { ans = ans * inv(2ll) % MOD; }
printf("%lld %lld", dif, ans);
}
J. Just Zeros
发现
然后我们来考虑修改。对于修改单点和列都是简单的,我们枚举
对于行我们不把他的影响考虑到列上而是行的状态上,维护一个标记
复杂度
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
#include <cstdio>
typedef int ll;
const int MAX = 15;
const int MAXX = 1e5 + 5;
const int INF = 0x3f3f3f3f;
using std::cin;
ll h, w, x, y, q, mn, tag;
bool a[MAX][MAXX];
ll ans[1 << 8], s[MAXX];
char c, _s[MAXX];
ll f(ll x) { ll res = 0; while (x) { x ^= (x & -x); ++res; } return res; }
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
cin >> h >> w >> q; mn = INF;
for (int i = 1; i <= h; ++i)
{
cin >> _s + 1;
for (int j = 1; j <= w; ++j) { a[i][j] = _s[j] - '0'; s[j] += (_s[j] - '0') << (i - 1); }
}
for (int S = 0; S <= (1 << h) - 1; ++S)
{
for (int i = 1; i <= w; ++i)
{
ll v = f(S ^ s[i]);
ans[S] += std::min(v, 1 + h - v);
}
mn = std::min(mn, ans[S] + f(S));
}
std::cout << mn << '\n';
while (q--)
{
mn = INF;
std::cin >> c;
if (c == 'R')
{
cin >> x;
tag ^= 1ll << (x - 1);
}
else if (c == 'K')
{
cin >> y;
for (int S = 0; S <= (1 << h) - 1; ++S)
{
ll v = f(s[y] ^ S);
ans[S] -= std::min(v, 1 + h - v);
}
s[y] ^= (1 << h) - 1;
for (int S = 0; S <= (1 << h) - 1; ++S)
{
ll v = f(s[y] ^ S);
ans[S] += std::min(v, 1 + h - v);
}
}
else
{
cin >> x >> y;
for (int S = 0; S <= (1 << h) - 1; ++S)
{
ll v = f(s[y] ^ S);
ans[S] -= std::min(v, 1 + h - v);
}
s[y] ^= 1ll << (x - 1);
for (int S = 0; S <= (1 << h) - 1; ++S)
{
ll v = f(s[y] ^ S);
ans[S] += std::min(v, 1 + h - v);
}
}
for (int S = 0; S <= (1 << h) - 1; ++S) { mn = std::min(mn, ans[S] + f(tag ^ S)); }
std::cout << mn << '\n';
}
}
K. Kindergarten Square
不难发现
#include <bits/stdc++.h>
#include <cstdio>
typedef long long ll;
ll a1, a2, a3, a4, T, h, w;
void solve()
{
scanf("%lld%lld%lld%lld", &a1, &a2, &a3, &a4);
w = a3 - a1;
h = std::ceil(1.0 * a4 / w);
if (a2 != a1 + 1 or a4 != a3 + 1) { printf("-1\n"); return ; }
if (w <= 0) { printf("-1\n"); return ; }
if ((a1 - 1) / w != (a2 - 1) / w or (a3 - 1) / w != (a4 - 1) / w) { printf("-1\n"); return ; }
printf("%lld %lld\n", h, w);
}
int main() { scanf("%lld", &T); while (T--) { solve(); } }
The 3rd Universal Cup. Stage 32: Dhaka
神秘比赛。成绩 2 / 13。
D. Qwiksort
考虑把序列分成两半,把前一半的都换到前面后一半都换到后面,然后分别排序。
先考虑偶数的情况。我们先把两半排序,然后把前一部分的后一半,和后一部分的前一半组成的序列排序,然后两半再排序再重复上述步骤,就一定能把前一半的都换到前面后一半都换到后面。正确性可以考虑 worst case
奇数不能恰好分出一半,要把前一部分多给出一个和后一部分多一个的情况都做一下。
发现奇数的情况恰好卡到
#include <bits/stdc++.h>
#include <cstdio>
typedef long long ll;
const int MAX = 5e6 + 5;
ll T, n, a[MAX];
void solve()
{
scanf("%lld", &n);
for (int i = 1; i <= 2 * n; ++i) { scanf("%lld", &a[i]); }
if (n & 1)
{
printf("10\n");
printf("%lld %lld\n%lld %lld\n", 1, n, n + 1, 2ll * n);
printf("%lld %lld\n", n / 2 + 1, n / 2 + n);
printf("%lld %lld\n", n / 2 + 2, n / 2 + n + 1);
printf("%lld %lld\n%lld %lld\n", 1, n, n + 1, 2ll * n);
printf("%lld %lld\n", n / 2 + 1, n / 2 + n);
printf("%lld %lld\n", n / 2 + 2, n / 2 + n + 1);
printf("%lld %lld\n%lld %lld\n", 1, n, n + 1, 2ll * n);
}
else {
printf("8\n");
printf("%lld %lld\n%lld %lld\n", 1, n, n + 1, 2ll * n);
printf("%lld %lld\n", n / 2 + 1, n / 2 + n);
printf("%lld %lld\n%lld %lld\n", 1, n, n + 1, 2ll * n);
printf("%lld %lld\n", n / 2 + 1, n / 2 + n);
printf("%lld %lld\n%lld %lld\n", 1, n, n + 1, 2ll * n);
}
}
int main()
{
scanf("%lld", &T); while (T--) { solve(); }
}
L. Uncle Bob and XOR Sum
一个选择方式
同时我们还要对
复杂度是
要特判
#include <bits/stdc++.h>
#include <cstdio>
typedef long long ll;
const int MAX = 1e5 + 5;
const int MAXX = 105;
const int MOD = 1e9 + 7;
ll n, T, k, ans;
ll a[MAX], b[MAXX];
ll popcnt(ll x) { ll res = 0; while (x) { x ^= x & -x; ++res; } return res; }
ll qp(ll a, ll x)
{
ll res = 1;
while (x) { if (x & 1) { res = res * a % MOD; } a = a * a % MOD; x >>= 1; }
return res;
}
struct B
{
ll siz = 0, a[35];
B() { siz = 0; for (int i = 0; i <= 31; ++i) { a[i] = 0; } }
void ins(ll x)
{
for (int i = 31; i >= 0; --i)
{
if (!x) { break; }
if (!(x & (1 << i))) { continue; }
if (!a[i]) { a[i] = x; ++siz; break; }
x ^= a[i];
}
}
bool f(ll x)
{
for (int i = 31; i >= 0; --i)
{
if (!(x & (1 << i))) { continue; }
x ^= a[i];
}
if (!x) { return true; }
else { return false; }
}
void clear() { siz = 0; for (int i = 0; i <= 31; ++i) { a[i] = 0; } }
} A, B;
void _solve(ll S)
{
B.clear(); ll bb = 0;
for (int i = 1; i <= k; ++i) { if (S & (1 << (i - 1))) { bb |= b[i]; } }
for (int i = 31; i >= 0; --i) { if (A.a[i]) { B.ins(bb & A.a[i]); } }
if (B.f(bb)) { ans = (ans + qp(-1, 1 + popcnt(S)) * qp(2, n - B.siz) % MOD + MOD) % MOD; }
}
void solve()
{
scanf("%lld%lld", &n, &k); ans = 0; A.clear();
for (int i = 1; i <= n; ++i) { scanf("%lld", &a[i]); A.ins(a[i]); }
for (int i = 1; i <= k; ++i) { scanf("%lld", &b[i]); }
for (int i = 1; i <= k; ++i) { if (b[i] == 0) { printf("%lld\n", 0ll); return ; } }
for (int S = 1; S <= (1 << k) - 1; ++S) { _solve(S); }
ans = (qp(2, n) - 1 - ans + MOD + MOD) % MOD;
printf("%lld\n", ans);
}
int main()
{
scanf("%lld", &T); while (T--) { solve(); }
}