经出题人同意,决定把此次比赛的题解放在此处供大家参考,如果有纰漏请及时反馈!
1212 刺激战场
题目:http://acm.guet.edu.cn/problem/view/1212.html
思路:
建立二分图:左半部分代表每行,右半部分代表每列。而行顶点i和列顶点j之间的连线就代表图 (i, j) 处有一个玩家。例如对于输入范例可建立以下二分图:
要求最少操作次数即求该二分图的最小点覆盖集,也就是最大匹配。这里使用了dinic算法求最大匹配,即先将二分图转化为网络流,再进行计算。
#include <bits/stdc++.h> using namespace std; const int kMax = 20000 + 10; const int kInf = 1e9; struct node { int v, cost; node(int _v, int _cost) : v(_v), cost(_cost) {} }; int s, t, n; int d[kMax]; vector<node> table[kMax]; bool bfs() { memset(d, -1, sizeof(d)); queue<int> q; q.push(s); d[s] = 0; while (!q.empty()) { int u = q.front(); q.pop(); for (int i = 0;i < table[u].size();++ i) { int v = table[u][i].v; if (table[u][i].cost > 0 && d[v] == -1) { q.push(v); d[v] = d[u] + 1; } } } return d[t] != -1; } int dfs(int u, int flow) { if(u == t) return flow; int res = 0; for (int i = 0;i < table[u].size();++ i) { int v = table[u][i].v; if (table[u][i].cost > 0 && d[v] == d[u] + 1) { int tmp = dfs(v, min(flow, table[u][i].cost)); flow -= tmp; table[u][i].cost -= tmp; res += tmp; for (int j = 0;j < table[v].size();++ j) { if (table[v][j].v == u) { table[v][j].cost += tmp; break; } } if (flow == 0) break; } } if (res == 0) d[u] = -1; return res; } int dinic() { int res = 0; while (bfs()) { res += dfs(s, kInf); } return res; } int main() { int m, a, b; scanf("%d%d", &n, &m); for (int i = 0;i < m;++ i) { scanf("%d%d", &a, &b); table[a].push_back({b + n, 1}); table[b + n].push_back({a, 0}); } s = 0; t = n + n + 1; for (int i = 1;i <= n;++ i) { table[s].push_back({i, 1}); table[i].push_back({s, 0}); table[i + n].push_back({t, 1}); table[t].push_back({i + n, 0}); } printf("%d ", dinic()); return 0; }
1213 监督学习
题目:http://acm.guet.edu.cn/problem/view/1213.html
思路:
标准答案应该是建立区间更新 + 区间查询的线段树,但是由于oj的限制导致数据量过小,以至于暴力也能过。对于暴力过的同学,出题人表示算你们运气好(#`O′)。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int kMax = 1e5 + 10; struct node { int l, r; ll sum, add; } tree[kMax << 2]; void pushup(int p) { tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum; } void pushdown(int p) { if (tree[p].add) { tree[p << 1].sum += (tree[p << 1].r - tree[p << 1].l + 1) * tree[p].add; tree[p << 1 | 1].sum += (tree[p << 1 | 1].r - tree[p << 1 | 1].l + 1) * tree[p].add; tree[p << 1].add += tree[p].add; tree[p << 1 | 1].add += tree[p].add; tree[p].add = 0; } } void build(int p, int l, int r) { tree[p].l = l; tree[p].r = r; tree[p].add = 0; if (l == r) { scanf("%lld", &tree[p].sum); return; } int m = (l + r) >> 1; build(p << 1, l, m); build(p << 1 | 1, m + 1, r); pushup(p); } ll query(int p, int l, int r) { if(l <= tree[p].l && r >= tree[p].r) return tree[p].sum; pushdown(p); int m = (tree[p].l + tree[p].r) >> 1; ll res = 0; if(l <= m) res += query(p << 1, l, r); if(r >= m + 1) res += query(p << 1 | 1, l, r); return res; } void update(int p, int l, int r, ll v) { if(l <= tree[p].l && r >= tree[p].r) { tree[p].sum += (tree[p].r - tree[p].l + 1) * v; tree[p].add += v; return; } pushdown(p); int m = (tree[p].l + tree[p].r) >> 1; if(l <= m) update(p << 1, l, r, v); if(r >= m + 1) update(p << 1 | 1, l, r, v); pushup(p); } int main() { int n, m, op, l, r; ll x; scanf("%d%d", &n, &m); build(1, 1, n); while (m --) { scanf("%d", &op); if(op == 1) { scanf("%d%d", &l, &r); printf("%lld ", query(1, l, r)); } else { scanf("%d%d%lld", &l, &r, &x); update(1, l, r, x); } } return 0; }
1214 年会抽大奖
题目:http://acm.guet.edu.cn/problem/view/1214.html
思路:
题意很简单,求组合数。解题需要用到数论中乘法逆元的知识,预处理出 n! 和 1/n! 的值。时间复杂度O(n + m)。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int kMax = 1e6 + 5; const ll kMod = 1e9 + 7; ll fac[kMax] = {1, 1}, inv[kMax] = {1, 1}, f[kMax] = {1, 1}; ll C(ll a, ll b) { if (a < b) return 0; return fac[a] * inv[b] % kMod * inv[a - b] % kMod; } void init() { for (int i = 2;i < kMax;++ i) { fac[i] = fac[i - 1] * i % kMod; f[i] = (kMod - kMod / i) * f[kMod % i] % kMod; inv[i] = inv[i - 1] * f[i] % kMod; } } int main() { int t; ll n, m; init(); scanf("%d", &t); while (t --) { scanf("%lld%lld", &n, &m); printf("%lld ", C(n, m)); } return 0; }
1215 教官的任务
题目:http://acm.guet.edu.cn/problem/view/1215.html
思路:
签到题,直接合并后排序。
#include <bits/stdc++.h> using namespace std; const int kMax = 205; int main() { int n, m, nums[kMax]; scanf("%d%d", &n, &m); for (int i = 0;i < n + m;++ i) { scanf("%d", &nums[i]); } sort(nums, nums + n + m); for (int i = 0;i < n + m;++ i) { if (i) printf(" "); printf("%d", nums[i]); } printf(" "); return 0; }
1216 区块链
题目:http://acm.guet.edu.cn/problem/view/1216.html
思路:
简单搜索题,dfs 一遍即可。
#include <bits/stdc++.h> using namespace std; const int kMax = 105; const int dx[] = {-1, 0, 1, 0, -1, -1, 1, 1}; const int dy[] = {0, -1, 0, 1, -1, 1, -1, 1}; int n, m; char table[kMax][kMax]; bool vis[kMax][kMax]; int dfs(int x, int y) { int res = 1; for (int i = 0;i < 8;++ i) { int tx = x + dx[i], ty = y + dy[i]; if (tx >= 0 && tx < n && ty >= 0 && ty < m && table[tx][ty] == '*' && !vis[tx][ty]) { vis[tx][ty] = true; res += dfs(tx, ty); } } return res; } int main() { int res = 0; scanf("%d%d", &n, &m); for (int i = 0;i < n;++ i) { scanf("%s", table[i]); } for (int i = 0;i < n;++ i) { for (int j = 0;j < m;++ j) { if (table[i][j] == '*' && !vis[i][j]) { vis[i][j] = true; res = max(res, dfs(i, j)); } } } printf("%d ", res); return 0; }