AtCoder Beginner Contest 378 題解

Showball發表於2024-11-12

AtCoder Beginner Contest 378 題解

比賽連結

A - Pairing 貪心

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

void Showball(){
    vector<int> a(5);
    for(int i=0;i<4;i++){
        int x;
        cin>>x;
        a[x]++;
    }
    int cnt=0;
    for(int i=1;i<=4;i++) cnt+=a[i]/2;
    cout<<cnt<<"\n";
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

B - Garbage Collection

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

void Showball(){
    int n;
    cin>>n;
    vector<int> q(n),r(n);
    for(int i=0;i<n;i++){
        cin>>q[i]>>r[i];
    }
    int m;
    cin>>m;
    while(m--){
        int x,d;
        cin>>x>>d;
        x--;
        cout<<d+(q[x]+r[x]-d%q[x])%q[x]<<"\n";
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

C - Repeating

使用 map 開個桶記錄一下每個數上次出現的位置即可。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

void Showball(){
    int n;
    cin>>n;
    vector<int> a(n);
    for(int i=0;i<n;i++){
        cin>>a[i];
    }
    map<int,int> mp;
    for(int i=0;i<n;i++){
        if(!mp[a[i]]) cout<<"-1 ";
        else cout<<mp[a[i]]<<" ";
        mp[a[i]]=i+1;
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

D - Count Simple Paths 深搜

資料範圍很小,直接列舉每個點,然後從每個點開始深搜即可。標準的深搜,寫法比較套路。注意 st陣列的更新。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

void Showball(){
    int h,w,k;
    cin>>h>>w>>k;
    vector<string> a(h);
    for(int i=0;i<h;i++){
        cin>>a[i];
    }
    vector<vector<int>> st(h,vector<int>(w));

    auto check=[&](int x,int y){
        return (0<=x&&x<h&&0<=y&&y<w&&a[x][y]=='.'&&!st[x][y]);
    };

    const int dx[]={1,-1,0,0};
    const int dy[]={0,0,1,-1};
    int ans=0;
    function<void(int,int,int)> dfs=[&](int x,int y,int cnt){
        if(cnt>=k){
            ans++;
            return;
        }
        for(int i=0;i<4;i++){
            int xx=x+dx[i],yy=y+dy[i];
            if(check(xx,yy)){
                st[xx][yy]=1;
                dfs(xx,yy,cnt+1);
                st[xx][yy]=0;
            }
        }
    };
    for(int i=0;i<h;i++){
        for(int j=0;j<w;j++){
            if(a[i][j]=='.') {
                st[i][j]=1;
                dfs(i,j,0);
                st[i][j]=0;
            }
        }
    }
    cout<<ans<<"\n";
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

E - Mod Sigma Problem 思維

如果兩層都要取模,那就非常簡單,只有一層,我們先不管取模嘗試化簡這個式子。

考慮使用字首和,原式 = $\sum_{1\le l,r \le N} ({S_r-S_{l-1}}) $ 。

繼續化簡: \(\sum_{1\le i \le N} \sum_{1\le j \le i} ({S_i-S_{j-1}})\) = \(\sum_{1\le i \le N} \sum_{1\le j \le i-1} ({S_i-S_{j}})\)

\(S_i\) 拿出來,即:\(\sum_{1\le i \le N} ({i*S_i-\sum_{1\le j\le i-1}S_{j}})\)

式子被我們化成了差的形式,因此,如果 \(S_i < S_j\) 那麼結果就會變成負數,因此我們就需要加上一個 \(m\)

考慮如何快速統計需要增加的 \(m\) 的數量。發現問題轉化為求在 \(s_i\) 之前有多少個數比它大。

其實就是經典的逆序對問題,使用歸併排序或者樹狀陣列即可解決。

這裡採用樹狀陣列,注意,這裡的陣列值要對 \(m\) 取模,值可能為 \(0\),因此,使用權值樹狀陣列時,可能會 TLE

所以,我們只需要偏移一位下標即可。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

void Showball(){
    int n,m;
    cin>>n>>m;
    vector<i64> a(n+1),s(n+1);
    for(int i=1;i<=n;i++){
        cin>>a[i];
        s[i]=(s[i-1]+a[i])%m;
    }

    vector<i64> tr(m+2);
    auto add=[&](int x){
        for(;x<=m;x+=x&-x) tr[x]++;
    };

    auto getsum=[&](int x){
        i64 ret=0;
        for(;x;x-=x&-x) ret+=tr[x];
        return ret;
    };

    i64 tot=0,res=0;
    for(int i=1;i<=n;i++){
        res+=i*s[i]-tot;
        tot+=s[i];
        res+=1LL*m*(getsum(m)-getsum(s[i]+1));
        add(s[i]+1);
    }
    cout<<res<<"\n";
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

F - Add One Edge 2 圖論

要構成滿足條件的環,我們只需要首尾點的度數為 \(2\) , 中間點的度數為 \(3\) 即可。發現度數為 \(3\) 的點組成的聯通塊周圍的度數為 \(2\) 的點兩兩都是可達的。

那麼我們只需要維護出每個點周圍有多少個度數為 \(2\) 的點,然後乘法原理即可。最後 \(BFS\) 統計一下答案即可。

#include<bits/stdc++.h>

using namespace std;

using i64=long long;

void Showball(){
    int n;
    cin>>n;
    vector<vector<int>> e(n);
    vector<int> d(n);
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        u--,v--;
        e[u].push_back(v);
        e[v].push_back(u);
        d[u]++;
        d[v]++;
    } 

    vector<int> w(n);
    function<void(int,int)> dfs=[&](int u,int fa){
        for(auto v:e[u]){
            w[u]+=(d[v]==2);
            if(v==fa) continue;
            dfs(v,u);
        }
    };

    auto bfs=[&](int st){
        int ret=0;
        queue<int> q;
        q.push(st);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            ret+=w[u];
            d[u]=0;
            for(auto v:e[u]){
                if(d[v]!=3) continue;
                q.push(v);
            }
        }
        return ret;
    };

    dfs(0,-1);
    
    i64 ans=0;
    for(int i=0;i<n;i++){
        if(d[i]==3){
            int t=bfs(i);
            ans+=1LL*t*(t-1)/2;
        }
    }
    cout<<ans<<"\n";
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t=1;
    //cin>>t;

    while(t--){
      Showball();
    }

    return 0;
}

相關文章