多源最短路。
#include
using namespace std;
const vector<int> dx = {1, 0, -1, 0};
const vector<int> dy = {0, 1, 0, -1};
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> dist(n, vector<int>(m, -1));
vector<string> board(n);
queue<pair<int, int>> q;
for (int i = 0; i < n; ++i) {
cin >> board[i];
for (int j = 0; j < m; ++j) {
if (board[i][j] == '#') {
dist[i][j] = 0;
q.emplace(i, j);
}
}
}
int ans = 0;
while (!q.empty()) {
int x = q.front().first, y = q.front().second;
ans = max(ans, dist[x][y]);
q.pop();
for (int d = 0; d < 4; ++d) {
int xx = x + dx[d], yy = y + dy[d];
if (xx >= 0 && xx < n && yy >= 0 && yy < m && dist[xx][yy] == -1) {
dist[xx][yy] = dist[x][y] + 1;
q.emplace(xx, yy);
}
}
}
cout << ans << "\n";
return 0;
}
注意到两维独立,然后倒着维护起始位置的合法区间即可。
#include
using namespace std;
bool solve(int n, int s, int len, string foo, string bar, char add, char sub) {
int l = 1, r = n;
for (int i = len - 1; ~i; --i) {
if (bar[i] == add) {
--l;
} else if (bar[i] == sub) {
++r;
}
l = max(l, 1);
r = min(r, n);
if (foo[i] == add) {
--r;
} else if (foo[i] == sub) {
++l;
}
if (l > r) {
return false;
}
}
return l <= s && s <= r;
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int r, c, n, x, y;
string foo, bar;
cin >> r >> c >> n >> x >> y >> foo >> bar;
cout << (solve(r, x, n, foo, bar, 'D', 'U') && solve(c, y, n, foo, bar, 'R', 'L') ? "YES" : "NO") << "\n";
return 0;
}
注意到每个人可以把直径减少 1 1 1 或者 2 2 2 ,然后判一下直径模 3 3 3 就行了。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<vector<int>> adj(n);
for (int i = 0; i < n - 1; ++i) {
int from, to;
cin >> from >> to;
--from;
--to;
adj[from].push_back(to);
adj[to].push_back(from);
}
vector<int> dist(n, -1);
function<void(int)> dfs = [&](int x) {
for (auto y : adj[x]) {
if (dist[y] == -1) {
dist[y] = dist[x] + 1;
dfs(y);
}
}
};
dist[0] = 0;
dfs(0);
int root = max_element(dist.begin(), dist.end()) - dist.begin();
dist.assign(n, -1);
dist[root] = 0;
dfs(root);
int diameter = *max_element(dist.begin(), dist.end());
cout << (diameter % 3 == 1 ? "Second" : "First") << "\n";
return 0;
}
注意到答案不大,记 f ( k , l , r , h ) f(k, l,r,h) f(k,l,r,h) 表示考虑上下边界为 l , r l,r l,r ,左边界为 h h h ,答案为 k k k 时最大可能的右端点。转移可以二分,也可以用单调性。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<string> board(n);
for (int i = 0; i < n; ++i) {
cin >> board[i];
}
vector<vector<vector<int>>> dp(n, vector<vector<int>>(n, vector<int>(m)));
for (int l = n - 1; ~l; --l) {
for (int r = l; r < n; ++r) {
for (int h = m - 1; ~h; --h) {
if (l == r || (board[l][h] == board[l + 1][h] && dp[l + 1][r][h] > h)) {
if (h + 1 < m && board[l][h] == board[l][h + 1]) {
dp[l][r][h] = dp[l][r][h + 1];
} else {
dp[l][r][h] = h + 1;
}
} else {
dp[l][r][h] = h;
}
}
}
}
int ans = 0;
while (dp[0][n - 1][0] < m) {
vector<vector<vector<int>>> new_dp(n, vector<vector<int>>(n, vector<int>(m)));
for (int l = n - 1; ~l; --l) {
for (int r = l; r < n; ++r) {
for (int h = m - 1; ~h; --h) {
new_dp[l][r][h] = dp[l][r][h] < m ? dp[l][r][dp[l][r][h]] : m;
if (l < r) {
int low = l, high = r - 1;
while (low <= high) {
int mid = (low + high) >> 1;
new_dp[l][r][h] = max(new_dp[l][r][h], min(dp[l][mid][h], dp[mid + 1][r][h]));
if (dp[l][mid][h] > dp[mid + 1][r][h]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
}
}
}
}
swap(dp, new_dp);
ans++;
}
cout << ans << "\n";
return 0;
}
特判全是一个字母的情况,假设这个串以 R R R 开头,不难发现:
然后就可以对着这个做DP了。
#include
using namespace std;
const int md = (int) 1e9 + 7;
inline void add(int &x, int y) {
x += y;
if (x >= md) {
x -= md;
}
}
inline void sub(int &x, int y) {
x -= y;
if (x < 0) {
x += md;
}
}
inline int mul(int x, int y) {
return (int) ((long long) x * y % md);
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
string s;
cin >> n >> m >> s;
if (s[0] == 'B') {
for (auto &c : s) {
c ^= 'R' ^ 'B';
}
}
int first = m;
for (int i = 0; i < m; ++i) {
if (s[i] == 'B') {
first = i;
break;
}
}
if (first == m) {
vector<int> dp(4);
dp[0] = dp[3] = 1;
for (int i = 1; i < n; ++i) {
vector<int> new_dp(4);
for (int a = 0; a < 2; ++a) {
for (int b = 0; b < 2; ++b) {
for (int c = 0; c < 2; ++c) {
if (!b || !c) {
add(new_dp[a * 2 + b], dp[a * 2 + c]);
}
}
}
}
swap(dp, new_dp);
}
int ans = 0;
for (int i = 0; i < 3; ++i) {
add(ans, dp[i]);
}
cout << ans << "\n";
return 0;
}
int last = m - 1;
while (s[last] == 'R') {
--last;
}
int limit = n, cur = 0;
for (int i = last; i >= -1; --i) {
if (i == -1 || s[i] == 'B') {
if (cur) {
if (cur & 1) {
limit = min(limit, cur + 1);
} else if (i == -1) {
limit = min(limit, cur + 2);
}
cur = 0;
}
} else {
++cur;
}
}
vector<int> dp(n + 1);
dp[0] = dp[2] = 1;
for (int i = 4; i <= n; i += 2) {
dp[i] = mul(dp[i - 2], 2);
if (i >= limit + 2) {
sub(dp[i], dp[i - (limit + 2)]);
}
}
int ans = 0;
for (int i = 2; i <= limit; i += 2) {
add(ans, mul(dp[n - i], i));
}
cout << ans << "\n";
return 0;
}
主要思路是,考虑压缩 G G G ,如果存在边 ( a , b ) , ( a , c ) (a,b), (a,c) (a,b),(a,c) 且在树上 a , b , c a,b,c a,b,c 按照这样的顺序排成了一条路径,那么删去 G G G 中的 ( a , c ) (a,c) (a,c) ,加上 ( b , c ) (b,c) (b,c) ,直到不能操作为止,那么我们有一个结论:
如果将图压缩了,答案就很好统计了。考虑压缩这个图,记 t o p ( a , b ) top(a,b) top(a,b) 表示以 a a a 为根,离 b b b 最近的与 a a a 直接相连的祖先,那么加一条边 ( a , b ) (a,b) (a,b) 时,如果 t o p ( a , b ) top(a,b) top(a,b) 存在 ,这条边就不用加,只需要加 ( b , t o p ( a , b ) ) (b,top(a,b)) (b,top(a,b)) ,否则将 b b b 子树打上标记,碰到边时压缩即可。具体细节可以参考代码。
#include
using namespace std;
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m;
vector<vector<int>> adj(n);
for (int i = 0; i < n - 1; ++i) {
int from, to;
cin >> from >> to;
--from;
--to;
adj[from].push_back(to);
adj[to].push_back(from);
}
vector<vector<int>> parent(n, vector<int>(n, -1));
for (int i = 0; i < n; ++i) {
function<void(int)> dfs = [&](int x) {
for (auto y : adj[x]) {
if (y != parent[i][x]) {
parent[i][y] = x;
dfs(y);
}
}
};
dfs(i);
}
vector<vector<bool>> graph(n, vector<bool>(n));
vector<vector<int>> top(n, vector<int>(n, -1));
function<void(int, int)> add = [&](int from, int to) {
if (top[from][to] == to || top[to][from] == from) {
return;
} else if (top[from][to] != -1) {
add(top[from][to], to);
} else if (top[to][from] != -1) {
add(top[to][from], from);
} else {
vector<pair<int, int>> edges;
for (int rep = 0; rep < 2; ++rep) {
graph[from][to] = true;
top[from][to] = to;
queue<int> q;
q.push(to);
while (!q.empty()) {
int x = q.front();
q.pop();
for (auto y : adj[x]) {
if (y != parent[from][x]) {
if (top[from][y] == -1) {
top[from][y] = to;
q.push(y);
} else if (graph[from][y]) {
graph[from][y] = graph[y][from] = false;
edges.emplace_back(to, y);
}
}
}
}
swap(from, to);
}
for (auto p : edges) {
add(p.first, p.second);
}
}
};
for (int i = 0; i < m; ++i) {
int from, to;
cin >> from >> to;
--from;
--to;
add(from, to);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
function<void(int, int)> dfs = [&](int x, int t) {
if (top[t][x] == x) {
++ans;
t = x;
}
for (auto y : adj[x]) {
if (y != parent[i][x]) {
dfs(y, t);
}
}
};
for (auto j : adj[i]) {
dfs(j, i);
}
}
cout << ans / 2 << "\n";
return 0;
}