2023ICPC南京 - A F G I
The 2023 ICPC Asia Nanjing Regional Contest
A 暴力
F 拓扑排序
G 01背包变式
I 思维
L 思维
A. Cool, It's Yesterday Four Times More
定义上下左右相邻的 '.' 为一个连通块
那么一个连通块里至少有一只袋鼠可以成为赢家,那么该连通块所有的袋鼠都可以成为赢家;如果一个连通块里至少有一只袋鼠不是赢家,那么该连通块的所有袋鼠都不是赢家。
注意到
int fx[4] = {0, 1, 0, -1}, fy[4] = {1, 0, -1, 0}; void solve(){ int n, m; cin >> n >> m; vector<string> ss(n); vector vis(n, vector<int>(m, 0)); for(int i = 0; i < n; ++ i) cin >> ss[i]; int ans = 0, idx = 0; for(int i = 0; i < n; ++ i){ for(int j = 0; j < m; ++ j){ if(ss[i][j] == '.' && vis[i][j] == 0){ queue<pii> q; vector<pii> path; q.push({i, j}); ++ idx; vis[i][j] = idx; while(q.size()){ auto [x, y] = q.front(); q.pop(); path.push_back({x - i, y - j}); for(int k = 0; k < 4; ++ k){ int xx = x + fx[k], yy = y + fy[k]; if(xx >= 0 && xx < n && yy >= 0 && yy < m && ss[xx][yy] == '.' && !vis[xx][yy]){ q.push({xx, yy}); vis[xx][yy] = idx; } } } bool f = true; for(int a = 0; a < n && f; ++ a){ for(int b = 0; b < m && f; ++ b){ if(vis[a][b] == idx || ss[a][b] != '.') continue; bool ok = true; for(auto [dx, dy] : path){ int x = a + dx, y = b + dy; if(x < 0 || x >= n || y < 0 || y >= m || ss[x][y] != '.'){ ok = false; break; } } if(ok){// ok - 没掉下去 f = false; } } } if(f) ans += path.size(); } } } cout << ans << '\n'; return ; }
F. Equivalent Rewriting
当某个下标最后一次出现时,其就是关键操作,即所有包含该下标的操作都需要在本操作的前面。
我们考虑对于所有的操作与其对应的关键操作建单向边,再跑拓扑排序,得到字典序尽可能大的拓扑序。如果最后得到的拓扑序不是1~n,则有解,反之无解
void solve(){ int n, m; cin >> n >> m; vector<int> p[n], b(m + 1, 0); for(int i = 0; i < n; ++ i){ int c; cin >> c; while(c--){ int a; cin >> a; p[i].push_back(a); b[a] = i; } } set<int, greater<int>> e[n]; vector<int> in(n, 0); for(int i = n - 1; i >= 0; -- i){ for(auto x : p[i]){ if(i != b[x] && !e[i].count(b[x])){ ++ in[b[x]]; e[i].insert(b[x]); } } } vector<int> q[2]; vector<int> ans; for(int i = 0; i < n; ++ i){ if(in[i] == 0) q[0].push_back(i); } int id = 0; while(ans.size() < n){ sort(q[id].begin(), q[id].end(), greater<int>()); for(auto u : q[id]){ ans.push_back(u); for(auto v : e[u]){ -- in[v]; if(in[v] == 0) q[1 - id].push_back(v); } } q[id].clear(); id = 1 - id; } bool f = false; for(int i = 0; i < n && !f; ++ i) if(i != ans[i]) f = true; if(f){ cout << "Yes\n"; for(int i = 0; i < n; ++ i) cout << ans[i] + 1 << " \n"[i == n - 1]; }else cout << "No\n"; return ; }
G. Knapsack
普通的01背包 + 可以免价格获得 k 件物品的价值
可以发现,最终带走的物品中,最贵的的 k 件物品一定是免价格获得的,不然就可以找到更优解
所以将所有物品按照价格升序,维护后缀取 k 件物品的最大价值和,前缀跑01背包,再枚举前后缀分界线,和的最大值即为最终答案
void solve(){ int n, w, k; cin >> n >> w >> k; vector<pii> a(n); for(auto &[x, y] : a) cin >> x >> y; sort(a.begin(), a.end()); vector<ll> dpk(n + 1, 0); multiset<int> s; if(k) for(int i = n - 1; i >= 0; -- i){ if(s.size() < k){ s.insert(a[i].second); dpk[i] = a[i].second; }else if(*s.begin() < a[i].second){ dpk[i] = a[i].second -*s.begin(); s.erase(s.begin()); s.insert(a[i].second); } dpk[i] += dpk[i + 1]; } ll ans = dpk[0]; vector<ll> dp(w + 1, 0); for(int i = 0; i < n; ++ i){ auto [x, y] = a[i]; for(int j = w; j >= x; -- j){ dp[j] = max(dp[j], dp[j - x] + y); } ans = max(ans, dp[w] + dpk[i + 1]); } cout << ans << '\n'; return ; }
I. Counter
对所有已知信息按照 a 升序排序,对于每一个数,考虑其与前一个已知条件
- 如果说值变大了,要么是一直选择按按钮 '+',要么选择一个时刻按按钮 'c'再一直按 '+'
- 如果说值变小了,一定是选择一个时刻按按钮 'c'再一直按 '+'
判断这两个条件是否对于所有排序后两两已知条件是否成立即可
void solve(){ int n, m; cin >> n >> m; vector<pii> a(m); for(auto &[x, y] : a) cin >> x >> y; a.push_back({0, 0}); sort(a.begin(), a.end()); for(int i = 1; i <= m; ++ i){ auto [x, y] = a[i]; auto [px, py] = a[i - 1]; if(y >= py && (x - px >= y + 1 || x - px == y - py)) continue; if(y <= py && x - px >= y + 1) continue; cout << "No\n"; return ; } cout << "Yes\n"; return ; }
L. Elevator
按照楼层从高到低尽可能的放货物
const int N = 1e5 + 5, inf = 0x3f3f3f3f; const ll INF = 0x3f3f3f3f3f3f3f3f, mod = 998244353; struct node{ int c, w, f; }p[N]; void solve(){ ll n, k; cin >> n >> k; for(int i = 0; i < n; ++ i) cin >> p[i].c >> p[i].w >> p[i].f; sort(p, p + n, [&](node x, node y){ return x.f > y.f; }); ll ans = 0; for(int i = 0; i < n; ++ i){ if(p[i].c == 0) continue; ans += p[i].f * (p[i].c * p[i].w / k); if(p[i].c * p[i].w % k){ ans += p[i].f; ll now = k - (p[i].c * p[i].w) % k; for(int j = i + 1; now && j < n; ++ j){ if(p[j].c * p[j].w <= now){ now -= p[j].c * p[j].w; p[j].c = 0; }else{ p[j].c -= now / p[j].w; now %= p[j].w; } } } } cout << ans << '\n'; return ; }
本文来自博客园,作者:Qiansui,转载请注明原文链接:https://www.cnblogs.com/Qiansui/p/17872196.html
分类:
oj - Codeforces
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!