vp过三题,c是交互题,想起了打华师大校赛时的不愉快经历了。
A.Li Hua and Maze
题意
给定一个n×m的矩阵,矩阵中有两个点(x1,y1),(x2,y2)。可以往矩阵中添加障碍物,使两个点之间不存在任何路径,求添加的障碍物数量最少为多少。
思路
可以把其中一个点的四周围起来,这样两点之间就不存在任何路径,所以答案最多为4。若有点的位置在边界处,所到的障碍就不到4个。
所以计算把两个点围起来所用的障碍物数量最少分别是多少,最终的答案就是较少的那个。
代码
void solve() {
int n, m;
cin >> n >> m;
int x1, y1, x2, y2;
cin >> x1 >> y1 >> x2 >> y2;
int t1 = (x1 != 1) + (x1 != n) + (y1 != 1) + (y1 != m),
t2 = (x2 != 1) + (x2 != n) + (y2 != 1) + (y2 != m);
cout << min(t1, t2) << "\n";
}
B.Li Hua and Pattern
题意
给定一个n×n的矩阵,其中每个元素为0或1。定义操作,把一个值为0的元素修改为1,或把一个值为1的元素修改为0。要求必须进行k次操作,使原矩阵变成一个中心对称的矩阵(即倒转180度后矩阵不变)。如果可以,输出YES,否则输出NO。
思路
遍历一遍,记录关于中心对称位置的元素不同的个数cnt。
如果n为奇数,那么只要cnt不大于k则为YES,否则为NO。因为修改完不同的元素后,剩下的操作次数全用于中心位置的元素即可,修改中心位置的元素不影响该矩阵是否符合要求。
如果n为偶数,则在cnt不大于k的同时,(k - cnt)还要为一个偶数才为YES。这是因为此时矩阵没有中心点,修改其它位置的元素会影响其对称性,所以需要剩余的修改次数为偶数,这样使得修改后还能在修改回去。
代码
void solve() {
int n ,k;
cin >> n >> k;
vector<vector<int>> v(n+1, vector<int>(n+1));
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
cin >> v[i][j];
}
}
int cnt = 0;
for(int i = 1; i <= n / 2; i++) {
for(int j = 1; j <= n; j++) {
if(v[i][j] != v[n-i+1][n-j+1]) cnt++;
}
}
if(n & 1) {
for(int i = 1; i <= n / 2; i++) {
if(v[n/2+1][i] != v[n/2+1][n-i+1]) cnt++;
}
if(cnt <= k) cout << "YES\n";
else cout << "NO\n";
}
else {
if(cnt <= k && (k - cnt) % 2 == 0) cout << "YES\n";
else cout << "NO\n";
}
}
C.Li Hua and Chess
题意
交互题,给定一个n×m棋盘和棋盘上的一个皇后,皇后一步可以向四周9个点移动。每次询问一个点,交互机给出皇后到这个点最短所需的步数,最多询问三次,确定皇后的位置。
思路
第一次询问点(1,1),得到步骤d1。如果d1为4,那么所有可能的点如下图:
我们再询问点(1,d1+1)和点(d1+1,1),即可确定皇后的位置。
但要注意,有时候d1+1大于n或m,需要特判。
代码
void solve() {
int n, m;
cin >> n >> m;
cout << "? 1 1" << "\n";
cout.flush();
int a1, a2 = 0, a3 = 0;
cin >> a1;
if(a1 == 0) {
cout << "! 1 1" << "\n";
cout.flush();
return;
}
if(a1 + 1 <= m) {
cout << "? 1 " << a1 + 1 << "\n";
cout.flush(); cin >> a2;
if(a2 == 0) {
cout << "! 1 " << a1 + 1 << "\n";
cout.flush();
return;
}
}
if(a1 + 1 <= n) {
cout << "? " << a1 + 1 << " 1" << "\n";
cout.flush(); cin >> a3;
if(a3 == 0) {
cout << "! " << a1 + 1 << " 1" << "\n";
cout.flush();
return;
}
}
if(a2 != 0 && a3 != 0) {
if(a2 == a1) {
cout << "! " << a1 + 1 << " " << a3 + 1 << "\n";
cout.flush();
} else {
cout << "! " << a2 + 1 << " " << a1 + 1 << "\n";
cout.flush();
}
} else {
if(a2 == 0) {
cout << "! " << a1 + 1 << " " << a3 + 1 << "\n";
cout.flush();
} else {
cout << "! " << a2 + 1 << " " << a1 + 1 << "\n";
cout.flush();
}
}
}
D. Li Hua and Tree
题意
给定一个有n个结点和n - 1条边的树,每个结点有权值。在一个结点的所有子结点中,子结点数最多的称为该结点的重子结点。
有两种操作,”1 x“表示输出以结点x为根节点为子树的权值和,”2 x“表示把x结点变成重子结点的子结点,而该重子结点则代替原来的x结点。
思路
模拟,用邻接表建树,dfs遍历得出每一个结点的子结点数量,子树权值和,父结点,用set维护每个结点的重子结点。操作1直接输出x的子树权值和,操作2按题意模拟即可。
代码
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e5+10;
struct node {
int siz, id;
bool operator <(const node &t) const {
if(siz != t.siz) return siz > t.siz;
return id < t.id;
}
};
set<node> s[N];
vector<int> adj[N];
vector<int> a(N), siz(N), p(N);
vector<ll> sum(N);
void dfs(int u, int fa) {
siz[u] = 1;
sum[u] = a[u];
for(int v : adj[u]) {
if(v == fa) continue;
p[v] = u;
dfs(v, u);
sum[u] += sum[v];
siz[u] += siz[v];
s[u].insert({siz[v], v});
}
}
int main () {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
dfs(1, -1);
while(m--) {
int opt, x;
cin >> opt >> x;
if(opt == 1) {
cout << sum[x] << "\n";
} else {
if(s[x].empty()) continue;
int son = s[x].begin()->id;
s[p[x]].erase({siz[x], x});
s[x].erase(s[x].begin());
int t_siz = siz[son];
ll t_sum = sum[son];
sum[son] = sum[x];
siz[son] = siz[x];
sum[x] -= t_sum;
siz[x] -= t_siz;
s[p[x]].insert({siz[son], son});
s[son].insert({siz[x], x});
p[son] = p[x];
p[x] = son;
}
}
return 0;
}
标签:结点,cin,int,siz,sum,864,Codeforces,Div.2,son
From: https://www.cnblogs.com/cenqi/p/17526773.html