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 ;
}
posted on 2023-12-02 20:37  Qiansui  阅读(103)  评论(0编辑  收藏  举报