Codeforces Round #490 (Div. 3) 訓練總結及題解

这场AB很快就秒了,C没读懂题,D也没读懂,开了E题,wa了2发之后休息了一会开始做CD,C很简单过了,然后继续怼E,wa了1发,开始看F然后开D,最后DE切换,后来发现Dwa3发是爆了int。E一直想拿并查集做懒得写tarjan,补题的时候发现了并查集的bug,最后还是拿了tarjan写。F题场后发现居然可以这样dp。
总结:

  1. 读不懂切题挺好的。
  2. 还是要注意ll
  3. D的前两wa,一个是TLE的算法没注意看数据,一个是没写好一个地方,也记录错误cnt里。

题解:
A.
题意:n个数,从两头删除权值<=k的数,问最多能删几个。(n,k都很小
思路:一开始写的双指针,后来写了一半想写deque了。
代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)

int main(){
    IO;
    int n,k;cin>>n>>k;
    deque<int>q;forn(i,n){
        int x;cin>>x;
        q.push_back(x);
    }int cnt = 0;
    while(!q.empty()){if(q.front()>k&&q.back()>k)break;
        if(q.front()<=k) cnt++,q.pop_front();
        else if(q.back()<=k) cnt++,q.pop_back();
        
    }
    cout << cnt <<'
';








    return 0;
}

B
題意:一個字符串長度n,設x屬於1-n,如果n可以被x整除那麽把1-x位倒過來。按因子逆序的方式倒過來的字符串是加密字符串,讓你解碼。(n很小
思路:暴力
代碼:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)

int main(){
    IO;
    int n;string s;cin>>n>>s;
    for1(i,n)if(n%i==0){
        string t = "";
        for(int j = i-1;j>=0;j--){
            t+=s[j];
        }
        for(int j = i;j<n;j++){
            t+=s[j];
        }
        s = t;
    }
    cout << s<<'
';

    return 0;
}

C
題意:長度為n的字符串,k次操作,先按字典序刪除字符再按1-n刪除的順序刪除k次,問之後最後字符串變成什麽。
思路:圖方便用的multiset
代碼:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)

int main(){
    IO;
    multiset<pair<char,int> >s;
    multiset<pair<int,char> > s2;
    int n,k;cin>>n>>k;
    forn(i,n){
        char c;cin>>c;
        s.insert({c,i});
    }
    forn(i,k){
        s.erase(s.begin());
    }
    for(auto x:s){
        s2.insert({x.second,x.first});
    }
    for(auto x:s2) cout <<x.second;
    return 0;
}

D.
題意:n被m整除,給你n個數,一次操作可以讓a[i]++,讓你使這n個數通過最小的操作次數使得每個a[i]%m后的數的cnt==n/m
思路:lowerbound
代碼:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(ll i=0;i<n;i++)
#define for1(i,n) for(ll i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const ll inf = 0x3f3f3f3f;
map<ll,ll>mp;

int main(){
    IO;
    ll n,m;cin>>n>>m;
    ll lim = n/m;
    vector<ll>a(n);
    set<ll>s;
    forn(i,m) s.insert(i);
    s.insert(inf);
    ll cnt = 0;
    forn(i,n){
        cin>>a[i];
        ll x = a[i]%m;
        if(mp[x]<lim) mp[x]++;
        else{
            if(*s.lower_bound(x)!=inf){
                ll y = *s.lower_bound(x);
                cnt+=y-x;
                a[i]+=y-x;
                mp[y]++;
                x = y;
            }else{
                ll y = *s.lower_bound(0);
                cnt+=m-(x-y);
                a[i]+=m-(x-y);
                mp[y]++;
                x = y;
            }
        }
        if(mp[x]==lim) s.erase(x);
    }cout<<cnt<<'
';
    for(auto x:a)cout<<x<<' ';









    return 0;
}

E.
題意:给定三个正整数N,M,S,N表示图中一共有N个节点,M表示有M条边,S是N个节点中的一个,并给定M条单向边,求至少需要加几条单向边,使得从S出发,能够到达所有其他节点。
思路:我是tarjan縮點完找入度為0的點
代碼:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 5005;
int low[maxn],dfn[maxn],id,color[maxn],cnt,tim[maxn];
vector<int>e[maxn],g[maxn];
stack<int>s;
void tarjan(int u){
    low[u] = dfn[u] = ++id;
    s.push(u);
    for(auto v:e[u]){
        if(!dfn[v]){
            tarjan(v);
            low[u] = min(low[u],low[v]);
        }else if(!color[v]){
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(low[u]==dfn[u]){
        cnt++;
        while(1){
            int x = s.top();s.pop();
            color[x] = cnt;
            if(x==u)break;
        }
    }
}

int main(){
    IO; 
    int n,m,s;cin>>n>>m>>s;
    forn(i,m){
        int u,v;cin>>u>>v;
        e[u].push_back(v);
    }
    tarjan(s);
    for1(i,n)if(!dfn[i]) tarjan(i);
    s = color[s];
    for1(i,n)if(!e[i].empty()){
        int u = color[i];
        for(auto x:e[i]){
            int v = color[x];
            if(u==v)continue;
            g[u].push_back(v);
            tim[v]++;
        }
    }
    int ans = 0;
    for1(i,cnt)if(!tim[i]&&i!=s) ans++;
    cout << ans <<'
';
    return 0;
}

F.
題意:n500個人,要從nk張權值範圍(1-1e5)的牌中抽取k10張,每個人有自己喜愛的牌fi,如果手中有x張自己喜愛的牌那麽他的貢獻就爲hx。問所有人貢獻最大值多少。
思路:預處理dp,因爲每個人貢獻只跟自己喜愛的牌有關,所以可以貪心,但是問題在於有幾個人遇到了相同的牌,比如h0-4為1 2 3 10000,現在兩個人都喜歡一張牌,那麽40分其實貢獻最高。
所以dp預處理dpij
i為i個人重複,j為n
m個物品
代碼:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define forn(i,n) for(int i=0;i<n;i++)
#define for1(i,n) for(int i=1;i<=n;i++)
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
const int maxn = 1e5+5;
int num[maxn],f[maxn],h[11],dp[505][5500];

int main(){
    IO;
    int n,k;cin>>n>>k;
    forn(i,n*k){
        int x;cin>>x;
        num[x]++;
    }
    forn(i,n){
        int x;cin>>x;
        f[x]++;
    }
    for1(i,k){
        cin>>h[i];
    }
    forn(i,n){
        forn(j,n*k){
            for1(z,k){
                dp[i+1][j+z] = max(dp[i+1][j+z],dp[i][j]+h[z]);
            }
        }
    }
    ll ans = 0;
    for1(i,maxn)if(f[i]){
        ans+=dp[f[i]][num[i]];
    }
    cout << ans <<'
';
    return 0;
}
人一我百,人十我万。
原文地址:https://www.cnblogs.com/AlexPanda/p/12520315.html