A. Li Hua and Maze
给出两个不相邻的点,最少需要堵上几个方格,才能使得两个方格之间不能互相到达。
思路:显然,对于不邻任何边界的方格来说,最少需要的是4,即上下左右都堵上;邻一个边界就-1,两个方格取一下最小值即可。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e5 + 5;
int t, n, m, x1, y1, x2, y2;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n >> m;
std::cin >> x1 >> y1 >> x2 >> y2;
int ans1 = 4, ans2 = 4;
if(x1 == 1 || x1 == n) ans1 --;
if(y1 == 1 || y1 == m) ans1 --;
if(x2 == 1 || x2 == n) ans2 --;
if(y2 == 1 || y2 == m) ans2 --;
std::cout << std::min(ans1, ans2) << '\n';
}
return 0;
}
B. Li Hua and Pattern
给出一个n*n的矩阵,操作k次,判断能否使得当前矩形和旋转180°后的图形完全相同。每次操作是将格子的颜色翻转。
思路:显然,将格子分为四部分,对角的两两都应该中心对称。对于两两方格的两个坐标有相加等于n+1的关系,判断一下即可。注意,对于奇数时的中心那一条,应该等于同一条线上的另一部分。对于操作次数k,如果小于不同的方格数,则一定不满足条件;若是大于k,n是偶数时,k-cnt必须是偶数。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e3 + 5;
int t, n, k;
int a[N][N];
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n >> k;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
std::cin >> a[i][j];
}
}
int m = (n + 1) / 2;
int cnt = 0;
for(int i = 1; i <= m; i ++) {
for(int j = 1; j <= m; j ++) {
if(a[i][j] != a[1 + n - i][1 + n - j])
cnt ++;
}
}
for(int i = m + 1; i <= n; i ++) {
for(int j = 1; j <= n / 2; j ++) {
if(a[i][j] != a[1 + n - i][1 + n - j])
cnt ++;
}
}
if(k < cnt || (k - cnt) % 2 && n % 2 == 0)
std::cout << "NO" << '\n';
else
std::cout << "YES" << '\n';
}
return 0;
}
C. Li Hua and Chess
在n*m的方格中猜一个确定位置。每次可以询问目标位置和询问位置的距离,注意,距离是指经过八个方向移动的最小距离,在三次询问之内找到答案。
思路:观察移动方式,我们可以发现对于一个位置来说,向外扩展x圈上的点到中心点的距离都是x。那么我们可以先询问(1, 1)点,然后再问(1 + len, 1 + len)点,当然,注意和边界的判断。那么答案位置必然在第二次询问的点的正上方或者正左方。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e3 + 5;
int t, n, m;
int ask(int x, int y) {
std::cout << "? " << x << ' ' << y << '\n';
std::cout.flush();
int dis;
std::cin >> dis;
return dis;
}
void work() {
int a = 1, b = 1;
int len = ask(a, b);
if(!len) {
std::cout << "! " << a << ' ' << b << '\n';
return;
}
a = std::min(a + len, n), b = std::min(b + len, m);
len = ask(a, b);
if(!len) {
std::cout << "! " << a << ' ' << b << '\n';
return;
}
if(len >= a && len < b) {
std::cout << "! " << a << ' ' << b - len << '\n';
return;
}
else if(len >= b && len < a) {
std::cout << "! " << a - len << ' ' << b << '\n';
return;
}
int aa = a, bb = b, ll = len;
a -= len;
len = ask(a, b);
if(!len) {
std::cout << "! " << a << ' ' << b << '\n';
return;
}
std::cout << "! " << aa << ' ' << bb - ll << '\n';
}
int main() {
// std::ios::sync_with_stdio(false);
// std::cin.tie(0);
// std::cout.tie(0);
std::cin >> t;
while(t --) {
std::cin >> n >> m;
work();
std::cout.flush();
}
return 0;
}
D. Li Hua and Tree
给出一棵树,进行k次操作,操作1是计算x的子树权值的和,操作2是交换x和x的重子,即断开x和x的父节点之间的边,链接x的重子和x的父节点之间的边。定义重子是x中具有最大子树的子节点,若有多个点满足条件,则重子是索引最小的那个点。
思路:模拟整个过程即可,注意细节。
AC Code:
#include <bits/stdc++.h>
typedef long long ll;
#define int long long
const int N = 1e5 + 5;
int n, m;
std::vector<int> vec[N];
int a[N], w[N], fa[N], val[N];
struct node{
int w, id;
bool operator<(const node &a) const{
if (w != a.w) return w > a.w;
return id < a.id;
}
};
std::set<node> s[N];
void DFS(int u, int f){
val[u] = a[u];
w[u] = 1;
for(auto v : vec[u]){
if (v == f) continue;
fa[v] = u;
DFS(v, u);
val[u] += val[v];
w[u] += w[v];
s[u].insert({w[v], v});
}
}
signed main(){
std::cin.tie(0);
std::cout.tie(0);
std::ios::sync_with_stdio(0);
std::cin >> n >> m;
for(int i = 1; i <= n; i ++) {
std::cin >> a[i];
}
for(int i = 1; i < n; i ++) {
int u, v;
std::cin >> u >> v;
vec[u].push_back(v);
vec[v].push_back(u);
}
DFS(1, -1);
while(m --){
int op, x;
std::cin >> op >> x;
if(op == 1)
std::cout << val[x] << '\n';
else {
if (s[x].empty()) continue;
int son = s[x].begin() -> id;
s[fa[x]].erase({w[x], x});
s[x].erase(s[x].begin());
int big = w[son];
int value = val[son];
w[son] = w[x];
w[x] -= big;
val[son] = val[x];
val[x] -= value;
s[fa[x]].insert({w[son], son});
s[son].insert({w[x], x});
fa[son] = fa[x];
fa[x] = son;
}
}
return 0;
}