Contest Environment
如果第二行有障碍,那么一定无解。
否则考虑将 A A A 移过去,对于连续的一段障碍,他需要直接跨越,然后其他人可以任意分配,像这样:
*#*A#.B
***...*
不难发现条件是 .
个数大于等于连续的一段 #
的个数加
3
3
3 。
#include <bits/stdc++.h>
using namespace std;
int main() {
int tt;
cin >> tt;
for (int ttt = 1; ttt <= tt; ++ttt) {
string foo, bar;
cin >> foo >> bar;
int n = foo.size();
bool flag = true;
for (int i = 0; i < n; ++i) {
if (bar[i] == '#') {
flag = false;
break;
}
}
if (!flag) {
cout << "Case #" << ttt << ": Impossible" << endl;
continue;
}
int free = 0;
for (int i = 0; i < n; ++i) {
if (foo[i] == '.') {
++free;
}
if (bar[i] == '.') {
++free;
}
}
int sum = 0, need = 0;
for (int i = 0; i < n; ++i) {
if (foo[i] == '#') {
++sum;
} else {
need = max(need, sum);
sum = 0;
}
}
cout << "Case #" << ttt << ": " << (free >= need + 3 ? "Possible" : "Impossible") << endl;
}
return 0;
}
Stockholm
不难发现一个区域的边界是,先走左儿子,然后一直走右儿子,或者先走右儿子,然后一直走左儿子。两个点的最短路一定会在lca处旁边的一个区域汇合。
#include <bits/stdc++.h>
using namespace std;
int main() {
int tt;
cin >> tt;
for (int ttt = 1; ttt <= tt; ++ttt) {
vector<map<long long, int>> dist(2);
for (int index = 0; index < 2; ++index) {
long long start;
cin >> start;
queue<long long> q;
auto insert = [&](long long x, int y) {
if (dist[index].find(x) == dist[index].end()) {
dist[index][x] = y;
q.push(x);
}
};
auto getl = [&](long long x) {
while (x > 0 && x % 2 == 1) {
x = x - 1 >> 1;
}
if (x <= 0) {
return -1ll;
} else {
return x - 2 >> 1;
}
};
auto getr = [&](long long x) {
while (x > 0 && x % 2 == 0) {
x = x - 2 >> 1;
}
if (x <= 0) {
return -2ll;
} else {
return x - 1 >> 1;
}
};
insert(start, 0);
insert(getl(start), 0);
insert(getr(start), 0);
while (!q.empty()) {
long long x = q.front();
q.pop();
insert(getl(x), dist[index][x] + 1);
insert(getr(x), dist[index][x] + 1);
}
}
int answer = INT_MAX;
for (auto p : dist[0]) {
if (dist[1].find(p.first) != dist[1].end()) {
answer = min(answer, p.second + dist[1][p.first]);
}
}
cout << "Case #" << ttt << ": " << answer << endl;
}
return 0;
}
Ethan Sums Shortest Distances
区间DP,如果有一条横着的割边,就划分成两个区间。否则这段区间只有一条竖边,枚举这条竖边计算即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
int tt;
cin >> tt;
for (int ttt = 1; ttt <= tt; ++ttt) {
int n;
cin >> n;
vector<vector<int>> board(2, vector<int> (n));
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < n; ++j) {
cin >> board[i][j];
}
}
vector<int> sum(n + 1);
for (int i = 0; i < n; ++i) {
sum[i + 1] = sum[i] + board[0][i] + board[1][i];
}
vector<vector<vector<vector<long long>>>> dp(n, vector<vector<vector<long long>>> (n, vector<vector<long long>> (2, vector<long long> (2, 1ll << 60))));
for (int l = n - 1; ~l; --l) {
for (int r = l; r < n; ++r) {
for (int a = 0; a < 2; ++a) {
for (int b = 0; b < 2; ++b) {
for (int m = l; m < r; ++m) {
for (int c = 0; c < 2; ++c) {
dp[l][r][a][b] = min(dp[l][r][a][b], dp[l][m][a][c] + dp[m + 1][r][c][b] + (long long)sum[m + 1] * (sum[n] - sum[m + 1]));
}
}
for (int m = l; m <= r; ++m) {
long long up = 0, down = 0;
for (int i = l; i <= r; ++i) {
up += board[0][i];
down += board[1][i];
}
long long ul = 0, ur = up;
long long dl = 0, dr = down;
if (!a) {
up += sum[l];
ul += sum[l];
} else {
down += sum[l];
dl += sum[l];
}
if (!b) {
up += sum[n] - sum[r + 1];
ur += sum[n] - sum[r + 1];
} else {
down += sum[n] - sum[r + 1];
dr += sum[n] - sum[r + 1];
}
long long value = up * down;
for (int i = l; i < r; ++i) {
ul += board[0][i];
ur -= board[0][i];
dl += board[1][i];
dr -= board[1][i];
value += ul * ur + dl * dr;
if (i < m) {
value += ul * down + dl * up;
} else {
value += ur * down + dr * up;
}
}
dp[l][r][a][b] = min(dp[l][r][a][b], value);
}
}
}
}
}
cout << "Case #" << ttt << ": " << dp[0][n - 1][0][0] << endl;
}
return 0;
}
Personal Space
一条鱼的活动范围可以写成下面的形式: ( l l , l , r , r r ) (ll, l, r, rr) (ll,l,r,rr) ,表示初始的活动范围是 ( l , r ) (l,r) (l,r) ,移除一个间隔之后活动范围是 ( l l , r ) (ll,r) (ll,r) 或者 ( l , r r ) (l,rr) (l,rr) 。不难发现这样的活动范围只有 O ( n ) O(n) O(n) 个,拿个set提取出来。考虑一个状态能转移到另一个状态当且仅当 r ≤ l l ′ , r r ≤ l ′ r\le ll', rr\le l' r≤ll′,rr≤l′ ,那么做个DP,对于每个 r r rr rr 维护最多放多少个和放最多鱼的前提下最小的 r r r 即可。
#include <bits/stdc++.h>
using namespace std;
int main() {
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
int n;
scanf("%d", &n);
vector<int> x(n), a(n), b(n);
vector<int> disc;
for (int i = 0; i < n; ++i) {
scanf("%d %d %d", &x[i], &a[i], &b[i]);
++b[i];
disc.push_back(x[i]);
}
disc.push_back(-1);
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
for (int i = 0; i < n; ++i) {
x[i] = lower_bound(disc.begin(), disc.end(), x[i]) - disc.begin();
}
set<int> s;
s.insert(-2);
s.insert(-1);
s.insert(0);
s.insert(disc.size());
s.insert(disc.size() + 1);
s.insert(disc.size() + 2);
vector<pair<int, pair<int, int>>> events;
for (int i = 0; i < n; ++i) {
events.emplace_back(a[i], make_pair(1, x[i]));
events.emplace_back(b[i], make_pair(0, x[i]));
}
sort(events.begin(), events.end());
vector<vector<pair<int, pair<int, int>>>> adj(disc.size() + 3);
vector<pair<int, int>> dp(disc.size() + 3);
for (auto p : events) {
int type = p.second.first;
int x = p.second.second;
if (!type) {
s.erase(x);
} else {
auto it = s.insert(x).first;
for (int i = 0; i < 3; ++i) {
--it;
}
vector<int> all(7);
for (int i = 0; i < 7; ++i) {
all[i] = *it;
++it;
}
for (int i = 0; i < 4; ++i) {
if (all[i] != -2 && all[i + 3] != disc.size() + 2) {
adj[all[i + 3]].emplace_back(all[i + 2], make_pair(all[i + 1], all[i]));
}
}
}
}
dp[0] = make_pair(0, 1);
for (int i = 1; i <= disc.size() + 2; ++i) {
dp[i] = dp[i - 1];
for (auto p : adj[i]) {
int x = p.first, y = p.second.first, z = p.second.second;
dp[i] = max(dp[i], make_pair(1, -x));
if (y >= 0 && -dp[y].second <= z) {
dp[i] = max(dp[i], make_pair(dp[y].first + 1, -x));
}
}
}
printf("Case #%d: %d\n", ttt, dp[disc.size() + 2].first);
}
return 0;
}
City Lights
建出关于星星的笛卡尔树,对于建筑物的限制,相当于树上一条链必须选至少一个。这个问题可以贪心做,把贪心的过程加上DP计数即可。
#include <bits/stdc++.h>
using namespace std;
const int md = 1e9 + 7;
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline int mul(int x, int y) {
return (long long)x * y % md;
}
int main() {
int tt;
cin >> tt;
for (int ttt = 1; ttt <= tt; ++ttt) {
int n, m;
cin >> n >> m;
vector<int> xn(n), yn(n), xm(m), ym(m);
vector<int> disc;
disc.push_back(0);
for (int i = 0; i < n; ++i) {
cin >> xn[i] >> yn[i];
disc.push_back(yn[i]);
}
for (int i = 0; i < m; ++i) {
cin >> xm[i] >> ym[i];
disc.push_back(ym[i]);
}
sort(disc.begin(), disc.end());
disc.erase(unique(disc.begin(), disc.end()), disc.end());
for (int i = 0; i < n; ++i) {
yn[i] = lower_bound(disc.begin(), disc.end(), yn[i]) - disc.begin();
}
for (int i = 0; i < m; ++i) {
ym[i] = lower_bound(disc.begin(), disc.end(), ym[i]) - disc.begin();
}
vector<int> p(m);
for (int i = 0; i < m; ++i) {
p[i] = i;
}
sort(p.begin(), p.end(), [&](const int &a, const int &b) {
return ym[a] < ym[b];
});
set<pair<pair<int, int>, int>> s;
vector<vector<int>> adj(m << 1 | 1);
vector<int> height(m << 1 | 1);
int total = 0;
s.insert(make_pair(make_pair(0, 1 << 30), total));
height[total++] = 1;
map<int, int> id;
for (auto i : p) {
int x = xm[i], y = ym[i];
auto it = --s.lower_bound(make_pair(make_pair(x + 1, 0), 0));
int left = it->first.first, right = it->first.second, index = it->second;
if (left <= x && right >= x) {
if (left < x) {
adj[index].push_back(total);
s.insert(make_pair(make_pair(left, x - 1), total));
height[total++] = y;
}
if (right > x) {
adj[index].push_back(total);
s.insert(make_pair(make_pair(x + 1, right), total));
height[total++] = y;
}
s.erase(it);
id[x] = index;
}
}
vector<vector<int>> event(total);
for (int i = 0; i < n; ++i) {
int x = xn[i], y = yn[i];
if (id.find(x) != id.end()) {
event[id[x]].push_back(y);
} else {
auto it = --s.lower_bound(make_pair(make_pair(x + 1, 1), 0));
event[it->second].push_back(y);
}
}
vector<vector<vector<int>>> dp(total, vector<vector<int>> (disc.size(), vector<int> (n + 1)));
vector<vector<vector<int>>> sum(total, vector<vector<int>> (disc.size() + 1, vector<int> (n + 1)));
vector<int> binary(n + 1);
binary[0] = 1;
for (int i = 0; i < n; ++i) {
binary[i + 1] = mul(binary[i], 2);
}
auto init = [&](int x) {
for (int i = 0; i < disc.size(); ++i) {
for (int j = 0; j <= n; ++j) {
sum[x][i + 1][j] = sum[x][i][j];
add(sum[x][i + 1][j], dp[x][i][j]);
}
}
};
function<void(int)> dfs = [&](int x) {
dp[x][0][0] = 1;
for (auto y : adj[x]) {
dfs(y);
init(x);
init(y);
vector<vector<int>> temp(disc.size(), vector<int> (n + 1));
for (int i = 0; i < disc.size(); ++i) {
for (int j = 0; j <= n; ++j) {
for (int k = 0; j + k <= n; ++k) {
add(temp[i][j + k], mul(dp[x][i][j], sum[y][i + 1][k]));
add(temp[i][j + k], mul(sum[x][i][j], dp[y][i][k]));
}
}
}
dp[x] = temp;
}
sort(event[x].begin(), event[x].end(), greater<int> ());
vector<vector<int>> temp(disc.size(), vector<int> (n + 1));
for (int i = 0; i <= event[x].size(); ++i) {
int value = i == event[x].size() ? 0 : event[x][i];
int ways = i == event[x].size() ? 1 : binary[event[x].size() - i - 1];
for (int j = 0; j < disc.size(); ++j) {
for (int k = 0; k <= n; ++k) {
add(temp[max(j, value)][k], mul(dp[x][j][k], ways));
}
}
}
dp[x] = temp;
for (int i = height[x]; i < disc.size(); ++i) {
for (int j = 0; j < n; ++j) {
add(dp[x][0][j + 1], dp[x][i][j]);
dp[x][i][j] = 0;
}
}
};
dfs(0);
int answer = 0;
for (int i = 1; i <= n; ++i) {
add(answer, mul(i, dp[0][0][i]));
}
cout << "Case #" << ttt << ": " << answer << endl;
}
return 0;
}
The Claw
令 h i h_i hi 表示 x i x_i xi 和 x i + 1 x_{i+1} xi+1 之间 y y y 的最大值,那么 a n s = 2 ( m + ∑ h i − ∑ y i ) ans = 2(m + \sum h_i - \sum y_i) ans=2(m+∑hi−∑yi) 。 h i h_i hi 改变当且仅当区间内有一个是区间最大值的往上提升了 1 1 1 。那么不难发现不同 y i y_i yi 之间的贡献是独立的,用线段树优化DP即可。
#include <bits/stdc++.h>
using namespace std;
class segtree_t {
public:
struct node_t {
int tag = 0, value = 0;
void apply(int l, int r, int v) {
tag += v;
value += v;
}
};
vector<node_t> tree;
int n;
node_t unite(const node_t &l, const node_t &r) {
node_t result;
result.value = max(l.value, r.value);
return result;
}
segtree_t(int n):n(n) {
tree.resize((n << 1) - 1);
}
inline void pull(int x, int z) {
tree[x] = unite(tree[x + 1], tree[z]);
}
inline void push(int x, int l, int r) {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
if (tree[x].tag) {
tree[x + 1].apply(l, y, tree[x].tag);
tree[z].apply(y + 1, r, tree[x].tag);
tree[x].tag = 0;
}
}
void build(int x, int l, int r) {
if (l != r) {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
build(x + 1, l, y);
build(z, y + 1, r);
pull(x, z);
}
}
template<typename T> void build(int x, int l, int r, const vector<T> &v) {
if (l == r) {
tree[x].apply(l, r, v[l]);
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
build(x + 1, l, y);
build(z, y + 1, r);
pull(x, z);
}
}
template<typename... T> void modify(int x, int l, int r, int ql, int qr, const T&... v) {
if (l == ql && r == qr) {
tree[x].apply(l, r, v...);
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
push(x, l, r);
if (qr <= y) {
modify(x + 1, l, y, ql, qr, v...);
} else if (ql > y) {
modify(z, y + 1, r, ql, qr, v...);
} else {
modify(x + 1, l, y, ql, y, v...);
modify(z, y + 1, r, y + 1, qr, v...);
}
pull(x, z);
}
}
template<typename... T> void modify(int l, int r, const T&... v) {
modify(0, 0, n - 1, l, r, v...);
}
node_t query(int x, int l, int r, int ql, int qr) {
if (l == ql && r == qr) {
return tree[x];
} else {
int y = l + r >> 1, z = x + (y - l + 1 << 1);
push(x, l, r);
if (qr <= y) {
return query(x + 1, l, y, ql, qr);
} else if (ql > y) {
return query(z, y + 1, r, ql, qr);
} else {
return unite(query(x + 1, l, y, ql, y), query(z, y + 1, r, y + 1, qr));
}
}
}
node_t query(int l, int r) {
return query(0, 0, n - 1, l, r);
}
};
int main() {
int tt;
scanf("%d", &tt);
for (int ttt = 1; ttt <= tt; ++ttt) {
int n, m;
scanf("%d %d", &n, &m);
vector<int> x(n), y(n);
vector<vector<int>> nodes(m);
vector<vector<pair<int, int>>> intervals(m);
for (int i = 0; i < n; ++i) {
scanf("%d %d", &x[i], &y[i]);
nodes[y[i]].push_back(x[i]);
}
long long answer = m;
int range = *max_element(x.begin(), x.end()) + 1;
int log_range = 0;
while (1 << log_range < range) {
++log_range;
}
vector<vector<int>> rmq(log_range, vector<int> (range));
for (int i = 0; i < n; ++i) {
rmq[0][x[i]] = max(rmq[0][x[i]], y[i]);
}
for (int i = 1; i < log_range; ++i) {
for (int j = 0; j + (1 << i) <= range; ++j) {
rmq[i][j] = max(rmq[i - 1][j], rmq[i - 1][j + (1 << i - 1)]);
}
}
vector<int> rmq_length(range);
for (int i = 2; i < range; ++i) {
rmq_length[i] = rmq_length[i >> 1] + 1;
}
auto query_max = [&](int l, int r) {
int k = rmq_length[r - l];
return max(rmq[k][l], rmq[k][r - (1 << k) + 1]);
};
for (int i = 0; i < n - 1; ++i) {
int l = min(x[i], x[i + 1]), r = max(x[i], x[i + 1]);
if (l > r) {
swap(l, r);
}
int height = query_max(l, r);
answer += height;
intervals[height].emplace_back(l, r);
}
for (int i = 0; i < n; ++i) {
answer -= y[i];
}
for (int i = 1; i < m; ++i) {
if (!nodes[i].empty()) {
sort(nodes[i].begin(), nodes[i].end());
vector<vector<int>> events(nodes[i].size());
for (auto &p : intervals[i]) {
p.first = lower_bound(nodes[i].begin(), nodes[i].end(), p.first) - nodes[i].begin();
p.second = upper_bound(nodes[i].begin(), nodes[i].end(), p.second) - nodes[i].begin() - 1;
if (p.first <= p.second) {
events[p.second].push_back(p.first);
++answer;
}
}
segtree_t segtree(nodes[i].size() + 1);
for (int j = 0; j < nodes[i].size(); ++j) {
segtree.modify(j + 1, j + 1, segtree.query(0, j).value + 1);
for (auto p : events[j]) {
segtree.modify(0, p, 1);
}
}
answer -= segtree.query(0, nodes[i].size()).value;
}
}
answer <<= 1;
printf("Case #%d: %lld\n", ttt, answer);
}
return 0;
}