2025河南ICPC省赛vp
摘要
6题691罚时,生擒金尾。
近期vp打的最好的一把,但是罚时还是有点太多了。
省赛在即,加油~ 快进到省赛带学长守银摄金~
赛时部分
B-最大popcount
位运算
特判当 \(n=0\) 时的情况,输出 \(0\) 即可。
其余情况则是形如 \(111111......11\) 的二进制表示下全是 \(1\) 的数。最便捷的方式就是求出 \(2^k-1\) 其中,\(k\) 为 \(n\) 在二进制表示下的位数。特别的注意当 \(n\) 已经在二进制表示下全为 \(1\) 的情况。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
set<ll> alls;
void get_pow() {
ll i;
for(i = 1; i <= 1e18; i *= 2) {
alls.insert(i);
}
alls.insert(i);
}
int main() {
int q;
cin >> q;
get_pow();
while(q--) {
ll n;
cin >> n;
if(n == 0) {cout << 0 << endl; continue;}
if(n == 1) {cout << 1 << endl; continue;}
if(alls.count(n+1)) {cout << n << endl; continue;}
ll cnt = 0;
while(n) {
cnt++;
n >>= 1;
}
cnt--;
ll ans = 1;
for(ll i = 1; i <= cnt; i ++ ) ans <<= 1;
cout << ans-1 << endl;
}
}
C-点对统计
并查集,数学
分析题目,属于连通块之间的联系问题,考虑用并查集解决。
但是枚举每两个点是否符合条件时间复杂度过高,无法通过该题,必须考虑优化。
通过分析,我们知道,对于一个点 \(i\) 我们在无向图 \(A\) 中属于连通块 \(a\),在无向图 \(B\) 中属于连通块 \(b\) 。而对于点 \(j\) 符合要求的条件是,\(j\) 在两个无向图中都与点 \(i\) 在同一个连通块中。因此我们考虑将这两个连通块组合离散化并记录在这个组合中有多少个点,然后对每一个组合里的点的数量计算两两组合的组合数加入答案即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+10;
int fa1[N], fa2[N];
int find1(int x) {
while(fa1[x] != x)
x = fa1[x] = fa1[fa1[x]];
return x;
}
bool merge1(int x, int y) {
x = find1(x);
y = find1(y);
if (x == y) {
return false;
}
fa1[y] = x;
return true;
}
int find2(int x) {
while(fa2[x] != x)
x = fa2[x] = fa2[fa2[x]];
return x;
}
bool merge2(int x, int y) {
x = find2(x);
y = find2(y);
if (x == y) {
return false;
}
fa2[y] = x;
return true;
}
bool same1(int x, int y) {
return find1(x) == find1(y);
}
bool same2(int x, int y) {
return find2(x) == find2(y);
}
#define pii pair<int, int>
#define ll long long
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int u, v;
int n, m1, m2;
cin >> n >> m1 >> m2;
for(int i = 1; i <= n; i ++ ) fa1[i] = fa2[i] = i;
while(m1--) {
cin >> u >> v;
merge1(u, v);
}
while(m2--) {
cin >> u >> v;
merge2(u, v);
}
map<pii, int> cnts;
for(int i = n; i >= 1; i -- ) {
int x = find1(i);
int y = find2(i);
cnts[{x, y}]++;
}
ll ans = 0;
for(auto &[p, cnt] : cnts) {
ans += (ll)cnt*(cnt-1)/2;
}
cout << ans << endl;
}
I-挡雨布
模拟,平面几何
我们知道,最终围成的图形一定是一个凸包,也就是说会由中间最高的柱子向两边下降。
我们可以在一开始输入的时候先维护最高点,然后分别从左和右往最高点方向遍历。每次从当前点延伸区间,当遇到第一个高于当前点的位置就计算部分面积加入答案,然后将该点设为当前点。
#include <bits/stdc++.h>
using namespace std;
#define pdd pair<double, double>
int main() {
int n;
cin >> n;
double x, y;
vector<pdd> node;
pdd ma = {0, -1};
for(int i = 0; i < n; i ++ ) {
cin >> x >> y;
if(y > ma.second) ma = {x, y};
node.push_back({x, y});
}
sort(node.begin(), node.end(), [](pdd& a, pdd& b){
return a.first <= b.first;
});
double ans = 0;
for(int i = 0; i < n; ) {
int j = i+1;
while(node[j].second < node[i].second && node[j].first < ma.first) j++;
double len = node[j].first-node[i].first;
ans += len*node[i].second + len*(node[j].second-node[i].second)/2;
if(node[j].first == ma.first) break;
i = j;
}
for(int i = n-1; i >= 0; ) {
int j = i-1;
while(node[j].second < node[i].second && node[j].first > ma.first) j--;
double len = node[i].first-node[j].first;
ans += len*node[i].second + len*(node[j].second-node[i].second)/2;
if(node[j].first == ma.first) break;
i = j;
}
printf("%.4lf", ans);
}
J-输入距离
排序
注意到最优方案是不能回头,所以只需要根据大小排序,然后从左往右枚举一遍计算答案即可。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int main() {
string str;
cin >> str;
int n = str.size();
vector<int> a(n);
for(int i = 0; i < str.size(); i ++ ) {
a[i] = str[i]-'a';
}
sort(a.begin(), a.end());
ll ans = 0;
int cnt = 0;
for(int i = 0; i < n; i ++ ) {
if(cnt == a[i]) ans++;
else {
ans += a[i]-cnt;
cnt = a[i];
ans++;
}
}
cout << ans << endl;
}
K-圆
dfs
注意到这题数据范围非常小,所以直接暴力枚举全排列找答案就可以了。
#include <bits/stdc++.h>
using namespace std;
#define pdd pair<double, double>
#define fi first
#define se second
int main() {
int n;
cin >> n;
double x, y, r;
vector<pdd> node(n);
for(int i = 0; i < n; i ++ ) {
cin >> x >> y;
node[i] = {x, y};
}
vector<double> rs(n);
for(int i = 0; i < n; i ++ ) {
cin >> rs[i];
}
// for(int i = 0; i < n; i ++ ) cout << node[i].fi << ' ' << node[i].se << endl;
// for(auto r : rs) cout << r << ' ';
// return 0;
vector<bool> vis(n, 0);
vector<int> res;
bool fg = 0;
auto dfs = [&](auto& dfs, int u) -> void{
if(res.size() > 1) {
for(int i = 0; i < res.size()-1; i ++ ) {
for(int j = i+1; j < res.size(); j ++ ) {
double x1 = node[i].fi, y1 = node[i].se;
double x2 = node[j].fi, y2 = node[j].se;
double r1 = rs[res[i]], r2 = rs[res[j]];
double len = sqrtl((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
if(len >= r1+r2 || len+min(r1, r2) <= max(r1, r2)) continue;
else return;
}
}
}
if(u >= n) {
if(res.size() == n) fg = 1;
return;
}
for(int i = 0; i < n; i ++ ) {
if(vis[i]) continue;
res.push_back(i);
vis[i] = 1;
dfs(dfs, u+1);
if(fg) return;
vis[i] = 0;
res.pop_back();
}
};
// cout << 1 << endl;
// return 0;
dfs(dfs, 0);
// cout << cnt << endl;
if(fg) {
for(int i = 0; i < n; i ++ ) cout << res[i]+1 << ' ';
} else {
cout << -1 << endl;
}
}
M-内存溢出
分类讨论,乘法
根据题目分类讨论即可。
#include <bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define ll long long
int main() {
ll x;
string str;
cin >> x >> str;
if(str == "MB") {
cout << x*1000 << ' ' << "KB";
} else {
cout << x*1024 << ' ' << "KiB";
}
}