我过了AB两题,C的大数据挂了,D的大数据没有提交,61分滚粗。。。
A. Dynamic Grid
这个非常简单啦。。每一次查询就对网格做一遍广搜,判断有多少连通分量;每一次查询就直接在二维数组上修改。判断连通分量个数用并查集也行,都不难。
#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int g[105][105];
const int dir_x[] = {1, -1, 0, 0};
const int dir_y[] = {0, 0, 1, -1};
int bfs(int row, int col) {
vector<vector<bool> > visited(row, vector<bool>(col, false));
int ret = 0;
FOR(i, row) FOR(j, col) {
if (g[i][j] == 1 && !visited[i][j]) {
++ret;
queue<pii> q;
q.push({i, j});
visited[i][j] = true;
while (!q.empty()) {
pii tp = q.front(); q.pop();
int r = tp.first, c = tp.second;
FOR(k, 4) {
int nr = r + dir_x[k], nc = c + dir_y[k];
if (nr < 0 || nr >= row || nc < 0 || nc >= col || g[nr][nc] == 0 || visited[nr][nc]) continue;
q.push({nr, nc});
visited[nr][nc] = true;
}
}
}
}
return ret;
}
void solve() {
int row, col, N;
cin >> row >> col;
FOR(i, row) {
string s;
cin >> s;
FOR(j, col) g[i][j] = s[j] - '0';
}
cin >> N;
char type;
int r, c, z;
FOR(i, N) {
cin >> type;
if (type == 'M') {
cin >> r >> c >> z;
g[r][c] = z;
}
else {
assert(type == 'Q');
printf("%d\n", bfs(row, col));
}
}
return;
}
int main() {
int TestCase;
cin >> TestCase;
FOR(caseID, TestCase) {
cout << "Case #" << caseID + 1 << ":" << endl;
solve();
}
return 0;
}
B. gBalloon
大意就是在所有的气球位置移动总和不超过Q的约束下,最快能多长时间全部运动到原点。考虑对最短时间t进行二分查找,如果t是一个可行解,那么就更新上界,否则更新下界。
那么怎么判断t是不是一个可行解呢?首先,气球之间是独立的,每个气球要想消耗最少的能量,肯定是要找距离当前位置最近的并且能卡进t的目标位置,这算是一种贪心策略。最后累加所有气球消耗的能量,如果超过Q则t为不可行解。
#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
void solve() {
int N, M, Q;
cin >> N >> M >> Q;
vector<int> v(M);
FOR(i, M) cin >> v[i];
vector<int> h(N), p(N);
FOR(i, N) { cin >> p[i] >> h[i]; }
double l = 0, r = 1e6;
while (r - l > 1e-6) {
double mid = (l + r) / 2.0;
int cur = 0;
bool ff = true;
FOR(i, N) {
bool ok = false;
if (p[i] == 0) continue;
FOR(d, M) {
int h1 = h[i] - d, h2 = h[i] + d;
if (h1 >= 0 && v[h1] * p[i] < 0 && mid >= -1.0 * (double)p[i] / v[h1]) {
cur += d;
ok = true;
break;
}
if (h2 < M && v[h2] * p[i] < 0 && mid >= -1.0 * (double)p[i] / v[h2]) {
cur += d;
ok = true;
break;
}
}
if (!ok || cur > Q) { ff = false; break; }
}
if (mid > 1e5 && !ff) {
cout << "IMPOSSIBLE" << endl;
return;
}
if (!ff) l = mid;
else r = mid;
}
cout << ceil(l) << endl;
return;
}
int main() {
int TestCase;
cin >> TestCase;
FOR(caseID, TestCase) {
cout << "Case #" << caseID + 1 << ": ";
solve();
}
return 0;
}
大意就是给一些ip段,把它们排序并且合并。并不涉及什么算法,也不难,就是实现起来比较繁琐,我比赛的时候写错了合并条件,大数据挂了。。。ip最好还是用32位数来表示,后续的各种判断都可以用位操作来实现,合并的条件有两个:
1.两个子网的prefix长度应该是相同的。
2.两个子网归一化之后只有prefix中最低位不同。
#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
struct Node {
vector<ll> ip;
ll cnt, rep;
Node() {
ip.resize(4);
}
void norm() {
ll rem = cnt;
rep = 0;
FOR(i, 4) {
if (rem >= 8) {
rem -= 8;
}
else if (rem == 0) {
ip[i] = 0;
}
else {
ll del = 8 - rem;
ll tt = ~((1 << del) - 1);
ip[i] = (tt & ip[i]);
rem = 0;
}
rep += ip[i] * (1 << (24 - 8 * i));
}
}
ll mx() {
return rep + ((1ll << (32 - cnt)) - 1);
}
ll mn() { return rep; }
};
bool cmp(const Node& a, const Node& b) {
FOR(i, 4) {
if (a.ip[i] < b.ip[i]) return true;
else if (a.ip[i] > b.ip[i]) return false;
}
return a.cnt < b.cnt;
}
bool check(Node& a, Node& b) {
return (a.mx() >= b.mx() && a.mn() <= b.mn());
}
void solve() {
ll N;
cin >> N;
vector<Node> arr(N);
FOR(i, N) {
char tmp;
FOR(j, 4) {
cin >> arr[i].ip[j] >> tmp;
}
cin >> arr[i].cnt;
arr[i].norm();
}
sort(arr.begin(), arr.end(), cmp);
ll ptr = 0;
vector<Node> res;
while (ptr < N) {
ll i = ptr + 1;
while (i < N && check(arr[ptr], arr[i])) ++i;
res.push_back(arr[ptr]);
ptr = i;
}
vector<Node> ans;
while (true) {
ans.clear();
ans.push_back(res[0]);
int ptr = 1;
while (ptr < (int)res.size()) {
if (res[ptr].cnt == res[ptr - 1].cnt && res[ptr - 1].mx() + 1 == res[ptr].mn()) {
ll tmp = (1ll << (32 - res[ptr - 1].cnt));
if (res[ptr - 1].mn() & tmp) {
ans.push_back(res[ptr++]);
continue;
}
--ans.back().cnt; ans.back().norm();
if (ptr + 1 < (int)res.size()) ans.push_back(res[ptr + 1]);
ptr += 2;
}
else {
ans.push_back(res[ptr]);
++ptr;
}
}
sort(ans.begin(), ans.end(), cmp);
if (ans.size() == res.size()) break;
res = ans;
}
arr = ans;
FOR(ptr, arr.size()) {
cout << arr[ptr].ip[0] << "." << arr[ptr].ip[1] << "."
<< arr[ptr].ip[2] << "." << arr[ptr].ip[3]
<< "/" << arr[ptr].cnt << endl;
}
return;
}
int main() {
ll TestCase;
cin >> TestCase;
FOR(caseID, TestCase) {
cout << "Case #" << caseID + 1 << ":" << endl;
solve();
}
return 0;
}
大意是喂兔子,但是长时间不喂兔子就会挂掉,人在上班和睡觉期间不能喂,问要保持兔子D天之后还活着,最少喂多少次。
对于小数据的话,可以一步步模拟,那在什么时候喂才能保证次数最少呢?用反证法可以证明应该贪心,在保证兔子不饿死的前提下尽可能晚得喂。如果找不出这样的时间点,那么兔子必然饿死。。。
对于大数据,一步一步模拟肯定要跪,一个key insight在于一天只有8万多秒,在喂了一定次数之后肯定会形成周期,以后的模式都是重复的。
我比赛的时候就是想到以上部分,小数据AC,大数据不晓得为什么总是偶尔会多算了1天。
坑就坑在边界上,最后一个周期最后一次喂是有可能不必要的,因为D天之后就不需要管它的死活了。。。所以就最后一个周期换成一步步模拟。
#include <bits/stdc++.h>
#define FOR(i, n) for (int i = 0; i < (int)n; ++i)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const ll MOD = 24 * 60 * 60;
ll get() {
ll ret = 0, val;
char cc;
cin >> val >> cc;
ret += val * 60 * 60;
cin >> val >> cc;
ret += val * 60;
cin >> val;
ret += val;
return ret;
}
void solve() {
ll G = get(), W = get(), H = get(), B = get(), X = get();
--W; --B;
ll D, cur = 0;
cin >> D;
D = D * MOD;
map<ll, ll> mp;
ll step = 1, res = 0;
bool first = true;
vector<ll> arr(MOD), pos(MOD, -1), ss(MOD);
FOR(i, MOD) {
if (i >= G && i <= W) arr[i] = i;
else if (i >= H && i <= B) arr[i] = i;
else if (i > W && i < H) arr[i] = W;
else if (i < G) arr[i] = B;
else if (i > B) arr[i] = B;
}
while (true) {
if (cur + X >= D) {
break;
}
ll up = cur + X;
ll feed = arr[up % MOD] + up / MOD * MOD;
if (feed > up) {
feed -= MOD;
assert(feed <= up);
}
if (feed < 0 || feed <= cur) {
cout << -1 << endl;
return;
}
ll rem = feed % MOD;
if (first && pos[rem] != -1) {
first = false;
ll len = feed - pos[rem], diff = step - mp[rem];
ll num = (D - feed) / len;
if (num == 0) {
cur = feed;
++res;
continue;
}
--num;
res += num * diff;
cur = feed + num * len;
++res;
}
else {
pos[rem] = feed;
ss[step] = feed;
mp[rem] = step++;
cur = feed;
++res;
}
}
printf("%lld\n", res);
}
int main() {
int TestCase;
cin >> TestCase;
FOR(caseID, TestCase) {
cout << "Case #" << caseID + 1 << ": ";
solve();
}
return 0;
}
总体上,这次题目有一点风格不一样,都是考智商和代码能力的。。。根本不是考算法。。