2025.5.18

刷题 摸鱼日记
这周末真的是忙到爆炸
周六下午队里集体vp了前几天的ICPC河南省赛,周日下午又去赶那个中国近代史纲要的视频作业
话说我演的角色是陈独秀先生,演技差到感觉可以算得上是侮辱先生的名誉了(
闲话少叙,这两天没刷1600,那就来看一下vp的情况吧
打的是这个:https://ac.nowcoder.com/acm/contest/110308 ,河南省第十六届ICPC大学生程序设计竞赛(重现赛)


vp时开了五道题,以我们的罚时来看,如果是赛时的话是可以拿银牌的
但毕竟vp是vp,状态和心态显然是远胜于比赛时的情况的,所以不能相提并论
以我们的实力,大一能有参赛机会就已经是万幸了,更别奢望什么牌子的了


这场有三个签到,B、J、M三道题
M题就是给定你一个内存的数值MB,让你转化为KB;如果是MiB,就转化为KiB
这要是再WA一发那简直不诗人了

点击查看代码
#include <bits/stdc++.h>

using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

void solve () {
    int n;
    std::string s;
    std::cin >> n >> s;

    if (s == "MiB") {
        std::cout << n * 1024 << " KiB";
    }
    else {
        std::cout << n * 1000 << " KB";
    }
}

int main () {   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t = 1;
    // std::cin >> t;

    while (t--) {
        solve();
    }
    
    return 0;
}


接下来开的是J题,给定一个字符串,判断需要多长时间才能打出来
由于他这个是“触摸屏”,最后可以拖动还不耗时,那其实直接sort一下然后输出每两个相邻字符的差值之和就可以了
看得出来前几个签到题确实就是拼手速的,直接秒掉

点击查看代码
#include <bits/stdc++.h>

using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

void solve () {
    std::string s;
    std::cin >> s;

    std::sort(s.begin(), s.end());
    ll ans = 0;
    for (int i = 0; i < s.size() - 1; i++) {
        ans += int(s[i + 1] - s[i]);
    }
    std::cout << ans + s.size() << '\n';
}

int main () {   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t = 1;
    // std::cin >> t;

    while (t--) {
        solve();
    }
    
    return 0;
}


第三道签到是B题,虽然还是签到,但这道题的难度比前两题高一些
但也没高多少(
这道题定义了一个函数f(x) = x在二进制下1的个数
也就是f(x) = cnt | while (x) if (x & 1) cnt++; x >>= 1;
这里需要让f(x)最大化,其中x <= n,输出f(x)取最大值时的x
这道题思路其实也很简单,如果n在二进制表示下全都是1,比如说7(10)= 111(2),那就直接输出7就可以了
反之,如果n在二进制表示下并非全都是1,比如说11(10)= 1011(2),那就输出111(2)的十进制表示
也就是len(n) - 1个1这个二进制数转化为十进制,其中len(n)意为n在二进制表示下的长度
vp时的思考时间略长了一点,但好在一遍过。只要没吃罚时,检查再长时间也是值得的
(ps: 从这道题开始就不是我单切了,而是和队友一起做的;所以我摒弃了我的陋习,加上了using namespace std,一切以团队为重)

点击查看代码
#include <bits/stdc++.h>
using namespace std;

using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

void solve () {
	ll n;
    cin >> n;
    
    ll m = 0, cnt = 0, n0 = n;
    while (n) {
        if (n & 1) cnt++;
        m++;
        n >>= 1;
    }
    // cout << m << ' ' << cnt << '\n';
    
    ll ans = 0;
    if (m != cnt) {
        for (int i = 1; i <= m - 1; i++) {
            ans *= 2;
            ans = ans + 1;
        }
        cout << ans << '\n';
    }
    else {
        cout << n0 << '\n';
    } 
}

int main () {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t = 1;
	cin >> t;
	
	while (t--) {
		solve();
	}
	
	return 0;
}


OK,到这里,3道签到题就开完了,接下来开始的是思维的较量!
当时我们看到I题过的比较多,然后就去开I题了
I.挡雨布

这道题其实不难,我们读完题就有思路了
其实就是找极值点就好了嘛,相当于找小山峰
找完之后,按照梯形面积的计算规则 S = ( 上底 + 下底 ) * 高 / 2 从前往后加起来就ok了
然后我快速的写了个结构体排序,喜提Wrong Answer

点击查看代码
#include <bits/stdc++.h>
using namespace std;

using ll = int64_t;
using ld = long double;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

typedef struct node {
    ld x, y;
    friend bool operator < (node a, node b) {
        if (a.x != b.x) return a.x < b.x;
        else return a.y > b.y;
    }
} node;

void solve () {
	int n;
    cin >> n;
    
    vector<node> a(n + 1);
    vector<node> b;
    
    for (int i = 1; i <= n; i++) {
        cin >> a[i].x >> a[i].y;
    }
    sort(a.begin() + 1, a.end());
    
    if (n == 1) {
        cout << "0\n";
        return;
    }
    
    int p = 1;
    b.push_back({a[1].x, a[1].y});
    while (p < n) {
        while (a[p + 1].y >= a[p].y) p++;
        b.push_back({a[p].x, a[p].y});
        while (a[p].y >= a[p + 1].y) p++;
    }
    b.push_back({a[n].x, a[n].y});
    
//     for (int i = 0; i < b.size(); i++) {
//         cout << "[" << b[i].x << ", " << b[i].y << "]\n";
//     }
    
    ld ans = 0.0;
    for (int i = 0; i < b.size() - 1; i++) {
        ans += (b[i + 1].x - b[i].x) * (b[i].y + b[i + 1].y) / 2;
    }
    cout << ans;
}

int main () {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t = 1;
	// cin >> t;
	
	while (t--) {
		solve();
	}
	
	return 0;
}


当时就是非常的不理解,样例都能过,队伍构造的好几组数据也都能过,我的队友也没找出来哪里有错,为啥WA了??
然后我们想到,会不会是存在y = 0的柱子没考虑到,于是改了一下,把y = 0的柱子统统剔除
结果还是WA了……

点击查看代码
#include <bits/stdc++.h>
using namespace std;

using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

typedef struct node {
    double x, y;
    friend bool operator < (node a, node b) {
        if (a.x != b.x) return a.x < b.x;
        else return a.y > b.y;
    }
} node;

void solve () {
	int n;
    cin >> n;
    
    vector<node> a(n + 1);
    vector<node> b;
    
    for (int i = 1; i <= n; i++) {
        cin >> a[i].x >> a[i].y;
    }
    sort(a.begin() + 1, a.end());
    
    if (n == 1) {
        cout << "0\n";
        return;
    }
    
    int p = 1;
    // if (a[1].y != 0) b.push_back({a[1].x, a[1].y});
    while (a[p].y == 0) p++;
    b.push_back({a[p].x, a[p].y});
    
    while (p < n) {
        while (a[p + 1].y >= a[p].y) p++;
        b.push_back({a[p].x, a[p].y});
        while (a[p].y >= a[p + 1].y) p++;
    }
    if (a[n].y != 0) b.push_back({a[n].x, a[n].y});
    
//     for (int i = 0; i < b.size(); i++) {
//         cout << "[" << b[i].x << ", " << b[i].y << "]\n";
//     }
    
    double ans = 0.0;
    for (int i = 0; i < b.size() - 1; i++) {
        ans += (b[i + 1].x - b[i].x) * (b[i].y + b[i + 1].y) / 2;
    }
    cout << ans;
}

int main () {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
	int t = 1;
	// cin >> t;
	
	while (t--) {
		solve();
	}
	
	return 0;
}


到这里的时候心态就有点崩了
虽然是vp,但我们vp前约好了,当作正式参赛来打;正式赛这麽WA两发,心态肯定是多少有点影响的
(毕竟你所做的每一个决定都在实时的影响着你的获奖情况a!)
然后队友认为是找点的过程出了问题,他想了一种找点方法,是从中心最高点处[ l, r ]的区间来向左向右扩散着来找
当时我迷信我的找点毫无缺陷,但是也过不了,所以交给队友了
然后队友在搓了二十多分钟后一发过了
事实证明我才是团队最大毒瘤 T_T
(ps: vp结束后问了一下别人,发现不需要处理y = 0的柱子)
队友的神仙代码如下

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<double, double> PII;
#define f first
#define s second
int n;
bool cmp(PII a, PII b)
{
    return a.s > b.s;
}
int main()
{
    cin >> n;
    vector<PII> q(n + 1);
    vector<PII> p(n + 1);
    for(int i = 1; i <= n; ++ i)
    {
        cin >> q[i].f >> q[i].s;
    }
    sort(q.begin() + 1, q.end(), cmp);
//     for(int i = 1; i <= n; ++ i)
//         cout << q[i].f << ' ' << q[i].s << endl;
    if(n == 1)
    {
        cout << 0;
        return 0;
    }
    else
    {
        double a1 = q[1].f, a2 = q[2].f;
        double l = min(a1, a2), r = max(a1, a2);
        int k = 0;
        p[k].f = q[1].f, p[k].s = q[1].s;
        ++ k;
        p[k].f = q[2].f, p[k].s = q[2].s;
        ++ k;
        for(int i = 3; i <= n; ++ i)
        {
            double x = q[i].f;
            if(x >= l && x <= r) continue;
            p[k].f = q[i].f, p[k].s = q[i].s;
            if(x > r) r = x;
            if(x < l) l = x;
            ++ k;
        }
        double ans = 0.0;
        sort(p.begin(), p.end());
        int j = n - k + 1;
        while(p[j].s == 0) j ++;
        int j1 = n;
        while(p[j1].s == 0) j1 --;
        
        for(int i = j + 1; i <= j1; ++ i)
        {
            ans += (p[i].s + p[i - 1].s) * (p[i].f - p[i - 1].f) / 2.0;
        }
        cout << ans;
    }
    return 0;
}


接下来第五道题,是我们vp时开出来的最后一道题
乍一看是一个计算几何,K.圆
给定一堆圆心和半径,现在你需要将他们一一匹配,使得没有任何两个圆是相交的
如上所言,第一眼以为是计算几何就没去管
后来发现过的人越来越多,才发现不对劲:n <= 10!!
好家伙,那不直接dfs就可以了?只要任意两个圆不满足 |r1 - r2| < d < r1 + r2 就可以了。然后我就直接上了
然后就开始了漫长的debug……


你猜怎么
我一直调没调过,队友写了一遍直接AC了
我果然是团队最大毒瘤啊啊啊啊啊
以后再也不会轻易的上机了 QAQ

点击查看代码
#include <bits/stdc++.h>

using ll = int64_t;
const ll N = 2e5 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

typedef struct node {
    double x, y;
} node;

std::vector<node> h(11);
std::vector<double> r(11);
std::vector<int> vis(11, 0);
std::vector<int> ans(11);
int flag = 0, n;

bool check (int h1, int r1, int h2, int r2) {
    double d = sqrtl((h[h1].x - h[h2].x) * (h[h1].x - h[h2].x) + (h[h1].y - h[h2].y) * (h[h1].y - h[h2].y));
    if ( fabs(r[r1] - r[r2]) < d && d < r[r1] + r[r2] ) return false;
    else return true;
}

void dfs (int step) {
    if (flag) return;

    if (step == n + 1) {
        for (int i = 1; i <= n; i++) {
            std::cout << ans[i] << " \n"[i == n];
        }
        flag = 1;
        return;
    }

    // find r to match the step
    for (int i = 1; i <= n; i++) {
        if (vis[i]) continue;

        int f = 1;
        for (int j = 1; j < step; j++) {
            if (!check(step, i, j, ans[j])) {
                f = 0;
                break;
            }
        }

        if (f) {
            vis[i] = 1;
            ans[step] = i;

            dfs(step + 1);

            vis[i] = 0;
            ans[step] = 0;
        }
    }

    return;
}

void solve () {
    std::cin >> n;

    for (int i = 1; i <= n; i++) {
        std::cin >> h[i].x >> h[i].y;
    }
    for (int i = 1; i <= n; i++) {
        std::cin >> r[i];
    }

    dfs(1);

    if (!flag) std::cout << -1 << '\n';
}

int main () {   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t = 1;
    // std::cin >> t;

    while (t--) {
        solve();
    }
    
    return 0;
}


接下来尝试开第六题,选的C.点对统计
这道题的意思是,给你两个无向图,让你找出来( x, y )的点对的数量,其中点x、y满足既在图1中连通也在图2中连通
vp时偷听到别人思路,说是并查集,正好队友擅长并查集,就让他写了
但是最后一步一直是O(n ^ 2),过不了,也不知道咋优化,就一直坐牢了
赛后看了下别人的代码,才恍然大悟
直接开一个map,以pair做键用来记录图1和图2的连通块
for循环的i从1到n,每次让map[ { find1( i ) , find2( i ) } ] ++
其中find1( i ) 就是点 i 在图1中所属的连通块编号,find2( i )就是点 i 在图2中所属的连通块编号
这样一来,所有连通情况就都记下来了!woc这思路简直是天才
代码如下

点击查看代码
#include <bits/stdc++.h>

using ll = int64_t;
const ll N = 2e6 + 10;
const ll INF = LLONG_MAX;
const ll mod = 998244353;

int f1[N], f2[N];

int find (int* f, int x) {
    if (x == f[x]) return x;
    else return f[x] = find(f, f[x]);
}

void solve () {
    int n, m1, m2;
    std::cin >> n >> m1 >> m2;

    for (int i = 1; i <= n; i++) {
        f1[i] = i;
        f2[i] = i;
    }

    for (int i = 1; i <= m1; i++) {
        ll u, v;
        std::cin >> u >> v;

        int fu = find(f1, u);
        int fv = find(f1, v);
        if (fu != fv) f1[fu] = fv;
    } 
    for (int i = 1; i <= m2; i++) {
        int u, v;
        std::cin >> u >> v;

        int fu = find(f2, u);
        int fv = find(f2, v);
        if (fu != fv) f2[fu] = fv;
    }

    std::map<std::pair<int, int>, ll> st;
    for (int i = 1; i <= n; i++) {
        int a = find(f1, i);
        int b = find(f2, i);
        st[{a, b}]++;
    }

    ll ans = 0;
    for (auto it : st) {
        ans += it.second * (it.second - 1) / 2;
    }
    std::cout << ans << '\n';
}

int main () {   
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t = 1;
    // std::cin >> t;

    while (t--) {
        solve();
    }
    
    return 0;
}


最终总结:还!得!练!
vp时出不来思路,赛时更不可能出
还有就是要一定要相信队友(
加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训加训

posted @ 2025-05-18 22:38  _彩云归  阅读(23)  评论(0)    收藏  举报