1. 程式人生 > 其它 >AtCoder Beginner Contest 216 A~F 題解

AtCoder Beginner Contest 216 A~F 題解

本場連結:AtCoder Beginner Contest 216

A - Signed Difficulty

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

int main()
{
    int x,y;scanf("%d.%d",&x,&y);
    printf("%d",x);
    if(y <= 2)  puts("-");
    else if(y >= 7) puts("+");
    return 0;
}

B - Same Name

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

const int N = 1005;
string s[N],t[N];

int main()
{
    Angel_Dust;
    int n;cin >> n;
    forn(i,1,n) cin >> s[i] >> t[i];
    forn(i,1,n) forn(j,1,i - 1) if(s[i] == s[j] && t[i] == t[j])    return cout << "Yes\n",0;
    cout << "No\n";
    return 0;
}

C - Many Balls

如果一開始就塞入60個B,那麼在倒數第\(i\)個後面插入一個A會產生 \(2^{i - 1}\)的貢獻,如此根據 \(n\) 的二進位制分解構造即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

int main()
{
    ll n;scanf("%lld",&n);
    string res;
    forr(i,0,59)
    {
        if(n >> i & 1)  res.push_back('A');
        res.push_back('B');
    }

    res.pop_back();
    
    cout << res << endl;
    return 0;
}

D - Pair of Balls

直接模擬.

維護:每一列數,某個顏色如果當前是末尾元素則他來自哪一列,某個元素是否已經被刪除,以及每個顏色當前的個數(在末尾位置).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

const int N = 2e5+7;
vector<int> E[N],ID[N];
int cnt[N];
bool del[N];


int main()
{
    int n,m;scanf("%d%d",&n,&m);
    set<pii> st;
    forn(i,1,m)
    {
        int k,x;scanf("%d",&k);
        forn(j,1,k) scanf("%d",&x),E[i].push_back(x);
        reverse(E[i].begin(),E[i].end());
        ++cnt[E[i].back()];
        ID[E[i].back()].push_back(i);
    }

    forn(i,1,n) if(cnt[i])  st.insert({cnt[i],-i});

    while(!st.empty())
    {
        auto _ = (*--st.end());st.erase(_);
        
        int c = _.x,delcol = -_.y;
        if(del[delcol]) continue;
        del[delcol] = 1;
        if(c == 1)  return puts("No"),0;
        int p1 = ID[delcol][0],p2 = ID[delcol][1];
        E[p1].pop_back();E[p2].pop_back();
        if(!E[p1].empty())
        {
            ++cnt[E[p1].back()];
            ID[E[p1].back()].push_back(p1);
        }
        if(!E[p2].empty())
        {
            ++cnt[E[p2].back()];
            ID[E[p2].back()].push_back(p2);
        }
        if(!E[p1].empty()) st.insert({cnt[E[p1].back()],-E[p1].back()});
        if(!E[p2].empty()) st.insert({cnt[E[p2].back()],-E[p2].back()});
    }

    puts("Yes");
    return 0;
}

E - Amusement Park

由於 \(k\) 很大,不能直接一步一步的去減,考慮加速每次減1的過程:當前取出的 \(a\) ,如果有多個合併.一直減到什麼時候會變化過程?把 \(a\) 減到僅小於他的那個元素 \(b\) 的時候,此時需要把兩組數進行合併.如此可以維護一個堆:堆內元素是<val,cnt>的形式.

每次取出堆頂的元素 \(<a,c>\),記上一個元素是 \(b\) ,考慮將 \(c\) 個元素一起減,那麼每次會減掉 \(c\),一共可以減 \(cir = \min(k / cnt(↓),a - b)\) 輪,會導致 \(k -= cir * c\),同時 $ a -= cir $.這一段每個 \(a\)會從 \(a\) 減到 \(a - cir\),所以是一個等差數列求和,一共有 \(c\) 個,答案累加即可.

其次,如果 \(a\) 在減少之後與 \(b\) 相同,則意味著這一輪已經進行完畢,可以和 \(b\) 進行合併,將堆內兩個元素進行合併即可.如果沒有,說明 \(k\)即使用完了也做不到使的 \(a\)\(b\) 相同,此時如果還有剩餘,就把最後的 \(k\) 也榨乾求貢獻.

特別的,為了方便實現,在堆內加入一個 \(<0,0>\).因為比 \(0\) 更小的沒有意義.

注意各種地方都會爆資料, \(a[i] \leq 2*10^9\)很麻煩.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

const int N = 1e5+7;
int a[N];

int main()
{
    int n,k;scanf("%d%d",&n,&k);
    forn(i,1,n) scanf("%d",&a[i]);
    sort(a + 1,a + n + 1);
    
    priority_queue<pii> pq;pq.push({0,0});
    forn(i,1,n)
    {
        int j = i;
        while(j + 1 <= n && a[j + 1] == a[i])   ++j;
        pq.push({a[j],j - i + 1});
        i = j;
    }

    ll res = 0;

    while(!pq.empty())
    {
        auto _ = pq.top();pq.pop();
        int a = _.x,c = _.y;
        if(a == 0)  break;
        int b = pq.top().x;
        int cir = min(k / c,a - b),r = a,l = a - cir + 1;
        if(l <= r)   res += c * 1ll * (l * 1ll + r) * (r * 1ll - l + 1) / 2;
        k -= 1ll * c * cir;
        a -= cir;
        if(a == b)  c += pq.top().y,pq.pop(),pq.push({b,c});
        else
        {
            res += 1ll * k * a;
            k = 0;
        }
        if(!k)  break;
    }
    printf("%lld\n",res);
    return 0;
}

F - Max Sum Counting

如果單純的考慮子集這個條件,想到即使使用 SOSDP 這樣的科技做子集求和也只能做到 \(O(n2^n)\) 的複雜度,而這個題可以承受的是 \(O(n^2)\) 顯然這樣不是一個合理的方向.考慮從 \(\max_{i \in S} A_i\)入手:可以想到次序無關,所以把所有二元組按照 \(a\) 不降的順序排序.現在考慮統計答案:

列舉 \(i\) 並且要求 \(a_i\) 就是左端的最大值,即剩下的元素只能在\([1,i - 1]\) 中選擇.現在條件可以寫成這樣: \(\sum_{j \in S} B_j + B_i \leq A_i\).可以再換寫成 \(\sum_{j \in S}B_j \leq A_i - B_i\).現在問題就等價於是在求\([1,i - 1]\)中選出若干下標,使得他們的和小於某個限制的方案數:每個元素要麼選要麼不選,這就是一個經典的01揹包求方案數問題,直接做即可.

於是有個問題,因為01揹包的複雜度是 \(O(nC)\),其中 \(C\) 是值域大小,如果直接去做的話會導致複雜度是 \(O(n^3)\) 的,但是 \(C\) 真的有必要是所有元素的值域嗎?因為限制是 \(A_i - B_i\) 所以限制其實是 \(O(n)\) 大小的.於是可以只做 \(C = N\) 的部分,因為限制只那麼多,這樣複雜度就是 \(O(n^2)\)的了.做完dp之後對答案求字首和就可以得到小於等於的方案數了.直接統計答案即可.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
#define forn(i,x,n) for(int i = x;i <= n;++i)
#define forr(i,x,n) for(int i = n;i >= x;--i)
#define Angel_Dust ios::sync_with_stdio(0);cin.tie(0)
#define x first
#define y second
mt19937_64 rng(chrono::high_resolution_clock::now().time_since_epoch().count());

const int N = 5005,MOD = 998244353;
int a[N],b[N],f[N][N],d[N];

bool cmp(int x,int y)
{
    if(a[x] != a[y])    return a[x] < a[y];
    return b[x] < b[y];
}

int main()
{
    int n;scanf("%d",&n);
    forn(i,1,n) d[i] = i;
    forn(i,1,n) scanf("%d",&a[i]);
    forn(i,1,n) scanf("%d",&b[i]);

    sort(d + 1,d + n + 1,cmp);

    f[0][0] = 1;
    forn(i,1,n)
    {
        forn(j,0,N - 1)
        {
            f[i][j] = f[i - 1][j];
            if(j >= b[d[i]])   f[i][j] = (f[i][j] * 1ll + f[i - 1][j - b[d[i]]]) % MOD;
        }
    }

    forn(i,0,n) forn(j,1,N - 1) f[i][j] = (1ll * f[i][j] + f[i][j - 1]) % MOD;

    int res = 0;
    forn(i,1,n)
    {
        if(a[d[i]] < b[d[i]]) continue;
        res = (res + 1ll * f[i - 1][a[d[i]] - b[d[i]]]) % MOD;
    }
    printf("%d\n",res);
    return 0;
}