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
定义上下左右相邻的 '.' 为一个连通块
那么一个连通块里至少有一只袋鼠可以成为赢家,那么该连通块所有的袋鼠都可以成为赢家;如果一个连通块里至少有一只袋鼠不是赢家,那么该连通块的所有袋鼠都不是赢家。
注意到 $ n, m \le 5 \times 10^3 $,所有暴力判断每只袋鼠是否能成为赢家即可。对于每只袋鼠利用 bfs 找其连通块,对剩下的非其连通块的袋鼠一步一步考虑其是否可能留下,若剩下的所有的袋鼠都不可留下,那么这个连通块的所有袋鼠都是赢家
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