AtCoder Beginner Contest 443

总括

题解包含A-F题,赛时快速切掉了ABCD四道题(第一次做到D),E题一直使用浮点运算,有一个测试点精度不够,赛后学习使用极性排序,突然发现F更容易出来

ScreenShot_2026-01-26_175249_268

A - Count .


思路

按照题意模拟即可

AC代码

    void solve(){
        string s;
        int ans = 0;
        cin >> s;
        for(auto i : s){
            if(i == 'i' || i == 'j')ans ++ ;
        }
        cout << ans << endl;
    }

B - Music Player


思路

根据题意模拟

AC代码

    void solve(){
        cin >> n;
        for(int i = 1; i <= n ; i ++ ){
            cin >> op;
            if(op == 1){
                num ++ ;
            }
            else if(op == 2){
                if(num >= 1) num -- ;
            }
            else {
                if(key)key = false;
                else key = true;
            }
            if(num >= 3 && key)cout << "Yes" << endl;
            else cout << " No" << endl;
        }
        
    }

C - Peer Review


思路

先统计出每个人可以审稿的人数 \(m_i\) ,计算\(C_{m_i}^{3}\)

AC代码

    void solve(){
        cin >> n >> m;
        for(int i = 1; i <= n ; i ++ ){
            mp[i] = n - 1;
        }
        for(int i = 1; i <= m; i ++ ){
            int a, b;
            cin >> a >> b;
            mp[a] --;
            mp[b] --;
        }
        for(int i = 1; i <= n ; i ++ ){
            int x = mp[i];
            if(x < 3){
                cout << 0 << " ";
            }
            else{
                cout << (x * (x - 1) * (x - 2)) / 6 << " ";
            }
        }
        cout << endl;
    }

D - Swap and Range Sum


思路

考察前缀和,对于每次操作等价于:

\[s[i] = s[i] - a[i] + a[i + 1] \]

然后交换 \(a[i]\)\(a[i + 1]\)

AC代码

    void solve(){
        s[0] = 0;
        cin >> n >> m;
        for(int i = 1; i <= n ; i ++ ){
            cin >> a[i];
            s[i] = s[i - 1] + a[i];
        }
        while(m -- ){
            int op;
            cin >> op;
            if(op == 1){
                int x;
                cin >> x;
                s[x] = s[x] - a[x] + a[x + 1];
                swap(a[x], a[x + 1]);
            }
            else{
                int l, r;
                cin >> l >> r;
                cout << s[r] - s[l - 1] << endl;
            }
        }
    }

E - Laser Takahashi


思路

考察极性排序

AC代码

    bool cmp(const P &a, const P &b){
        int ah = (a.y < 0 || (a.y == 0 && a.x > 0));
        int bh = (b.y < 0 || (b.y == 0 && b.x > 0));
        if(ah != bh)return ah > bh;
        else return a.y * b.x > b.y * a.x; 
    }

    void solve(){
        int n, q;
        cin >> n >> q;
        for(int i = 1; i <= n ; i ++ ){
            cin >> p[i].x >> p[i].y;
        }
        for(int i = 1; i <= n ; i ++ ){
            num[i] = i;
        }
        sort(num + 1, num + 1 + n, [&](int i, int j){return cmp(p[i], p[j]);});
        for(int i = 1; i <= n ; i ++ ){
            rev[num[i]] = i;
        }
        cout << endl;
        l[1] = 1;
        for(int i = 2; i <= n ; i ++ ){
            if(cmp(p[num[i - 1]], p[num[i]]))l[i] = i;
            else l[i] = l[i - 1];
        }
        r[n] = n;
        for(int i = n - 1; i >= 1 ; i -- ){
            if(cmp(p[num[i]], p[num[i + 1]]))r[i] = i;
            else r[i] = r[i + 1];
        }
        while( q -- ){
            int a, b;
            cin >> a >> b;
            a = l[rev[a]];
            b = r[rev[b]];
            if(a < b)cout << b - a + 1 << endl;
            else cout << n - a + b + 1 << endl;
        }    

    }

F - Diagonal Separation 2


思路

dp,我们考虑合法状态下每一行的白色格子的个数,不难发现每一行白色格子的数量应当是递减的,这类似于一个阶梯状

我们定义 \(dp[i][j]\) 表示来到第i行,本行最终状态下白色格子数量为 \(j\) 个,的最少操作数

状态转移:

\[min_{k=j}^n\left\{dp[i-1][k]\right\}+j-pre[i][j]+suf[i][j+1] \]

其中, \(pre[i][j]\) 表示第 \(i\) 行从 \(1\)\(j\) 位置白色的数量, \(suf[i][j]\) 表示第 \(i\) 行从\(j + 1\)\(n\) 位置白色的数量

但是这样是不行的,因为如果要枚举 \(k\) ,时间复杂度就会变成 \(O(n^3)\),因此我们考虑维护一个变量来记录上一行的后缀最小值, \(j\) 从大到小枚举,即可实现 \(O(n^2)\)

AC代码


posted @ 2025-11-29 17:02  Oaths  阅读(22)  评论(0)    收藏  举报