1. 程式人生 > 其它 >Educational Codeforces Round 115 (Rated for Div. 2)(題解)

Educational Codeforces Round 115 (Rated for Div. 2)(題解)

Educational Codeforces Round 115 (Rated for Div. 2)

A - Computer Game

思路:dfs暴力搜尋。

\(Code:\)

const int N = 1010;
int _,n;
char  a[3][N];
bool st[3][N];
int l[] = {0,0,-1,1,1,1,-1,-1},r[] = {1,-1,0,0,1,-1,1,-1};
void dfs(int x,int y){
    
    st[x][y] = true;
    for(int i=0;i<=7;++i){
        int idx = l[i] + x,idy = r[i] + y;
        if(idx > 2 or idx <= 0 or idy > n or idy <= 0)continue;
        if(st[idx][idy])continue;
        if(a[idx][idy] == '1')continue;
        dfs(idx,idy);
    }
}
void solve(){
    read(_);
    while(_--){
        read(n);
        memset(a,-1,sizeof a);
        memset(st,0,sizeof st);
        scanf("%s%s",a[1]+1,a[2]+1);
        if(a[1][1] == '0')
        dfs(1,1);
        if(st[2][n] == 1)puts("YES");else puts("NO");
    }
}

B - Groups

思路:暴力列舉答案,然後設答案為\((l,r)\) 那麼檢測答案是否合法。只有當所有人都至少在\((l,r)\)其中一天內有空,並且在這兩天有空的總人數均大於等於\(\frac{n}{2}\) ,那麼答案就合法。如果不理解可以自己畫圖感受一下。

\(Code:\)

const int N = 100100;
int _,n;
int  a[N][6];
bool st[33];
int l[] = {0,0,-1,1,1,1,-1,-1},r[] = {1,-1,0,0,1,-1,1,-1};
 
void solve(){
    read(_);
    while(_--){
        read(n);
        rep(i,1,n){
            rep(j,1,5){
                read(a[i][j]);
            }
        }
        bool flag = false;
        for(int i=1;i<(1<<5);++i){
            int cnt = 0;
            int now[55],hh = 0;
            for(int j=0;j<=4;++j){
                if((i>>j)&1){
                    now[++hh] = j+1;cnt++;
                }
            }
            bool judge = true;
            int x[33];x[1] = x[2] = 0;
            if(cnt == 2){
                //rep(j,0,4)write((i>>j)&1);pc('\n');
                for(int j=1;j<=n;++j){
                    if(!a[j][now[1]] and !a[j][now[2]]){
                        judge = false;break;
                    }
                    x[1] += (a[j][now[1]] == 1);
                    x[2] += (a[j][now[2]] == 1);
                }
            }else continue;
            if(x[1] < n/2 or x[2] < n/2)judge = false;
            if(judge){flag = true;break;}
        }
        if(flag)puts("YES");else puts("NO");
    }
}

C - Delete Two Elements

思路:因為他讓選擇兩個數,我們首先判斷是否有答案,由平均數定義可得:

\[\frac{\sum_1^n a[i]}{n} = \frac{a[x] + a[y]}{2} \]

所以:

\[a[x] + a[y] = \frac{2\sum_1^na[i]}{n} \]

因為陣列\(a\)是整數,那麼首先判斷是否\(a[x] + a[y]\)可以為整數,然後開個桶記錄所有個數,依次列舉每一個\(a[i]\)作為答案。即可得到答案

複雜度\(O(nlogn)\)

\(Code:\)

const int N = 200100;
int _,n;
ll  a[N];
bool st[33];
int l[] = {0,0,-1,1,1,1,-1,-1},r[] = {1,-1,0,0,1,-1,1,-1};
map<ll,ll>S;
void solve(){
    read(_);
    while(_--){
        S.clear();
        read(n);
        ll Sum = 0ll;
        rep(i,1,n){
            read(a[i]);S[a[i]]++;
            Sum += a[i];
        }
        if((Sum * (2ll))%n){
            puts("0");continue;
        }
        ll x = (Sum * (2ll))/n;
        //printf("%lld\n",x);
        ll ans = 0;
        rep(i,1,n){
            ll now = x - a[i] ;
            ll res = S[now];
            if(now == a[i]){
                res--;
            }
            ans+=res;
        }
        write(ans/2);pc('\n');
    }
}

D - Training Session

思路:這個題是讓我們選合法三元組數,那麼正難則反,我們考慮不符合的個數。首先我們發現如果三元組有一個屬性全部相同,那麼該三元組一定合法。比如屬性\(A[x,x,x]\) ,那麼屬性\(B\)一定是不相同的,因為如果相同就不滿足題目所給限制:任意兩個問題\(A\)屬性和\(B\)屬性至少有一個不同。那麼我們不符合的個數裡只有這種形式\(A[x,x,y],B[a,b,b]\),即兩個屬性是兩個相同的,那我們發現我們可以列舉作為中間的,因為在中間的元素右邊必定與其\(A\)屬性相同,左邊必定與其\(B\)屬性相同,開個桶分別記錄兩個屬性滿足的個數,所以不符合的個數應該是兩邊相乘得到的結果。最後用總個數減去不符合的個數。

複雜度\(O(n)\)

\(Code:\)

const int N = 200100;
int _,n;
ll  a[N];
bool st[33];
int l[N],r[N];
map<ll,ll>S;
pair<int,int>arr[N];
void solve(){
    read(_);
    while(_--){
        read(n);
        fill(l,l+1+n,0);fill(r,r+1+n,0);
        ll sum = (1ll *n*(n-1)*(n-2))/6;
        rep(i,1,n){
            int x,y;
            read(x);read(y);
            l[x]++;r[y]++;
            arr[i] = {x,y};
        }
        ll res = 0ll;
        rep(i,1,n){
            int x = arr[i].first,y = arr[i].second;
            res += 1ll*(l[x]-1)*(r[y]-1);
        }
        write(sum-res);pc('\n');
    }
}

E - Staircases

思路:這個題感覺比較好想,重點應該在程式碼實現上,將梯子向上、向下搜尋。然後在第一個不自由的格子停住,然後相乘維護答案。因為詢問\(10^4\),並且每次詢問暴力複雜度為\(O(n)\) 總複雜度為\(O(n*q)\) ,可以通過。

\(Code:\)

const int N = 1010;
int _, n, m, q;
bool st[N][N];
ll a[N];
int l[] = {-1, 0, 1, 0}, r[] = {0, -1, 0, 1};
ll ans;
void work(int x, int y) {
    // 2~3 1~4
    int idx = x, idy = y;
    ll a1 = 0, a2 = 0, b1 = 0, b2 = 0;
    int now = 0;
    bool tmp = st[x][y];
    st[x][y] = false;
    while (idx >= 1 and idx <= n and idy >= 1 and idy <= m and
           st[idx][idy] == false) {
        a1++;
        idx += l[now];
        idy += r[now];
        now++;
        if (now >= 2) now = 0;
    }
    now = 1;
    idx = x, idy = y;
    while (idx >= 1 and idx <= n and idy >= 1 and idy <= m and
           st[idx][idy] == false) {
        a2++;
        idx += l[now];
        idy += r[now];
        now++;
        if (now >= 2) now = 0;
    }
    now = 2;
    idx = x, idy = y;
    while (idx >= 1 and idx <= n and idy >= 1 and idy <= m and
           st[idx][idy] == false) {
        b2++;
        idx += l[now];
        idy += r[now];
        now++;
        if (now >= 4) now = 2;
    }
    idx = x;
    idy = y;
    now = 3;
    while (idx >= 1 and idx <= n and idy >= 1 and idy <= m and
           st[idx][idy] == false) {
        b1++;
        idx += l[now];
        idy += r[now];
        now++;
        if (now >= 4) now = 2;
    }
    if (tmp == true) {
        ans += a1 * b1 + a2 * b2 - 1;
    } else {
        ans -= a1 * b1 + a2 * b2 - 1;
    }
    st[x][y] = !tmp;
}
void solve() {
    read(n);
    read(m);
    read(q);
    rep(i, 1, max(n, m)) {
        ll x1 = max(1ll * (n - i + 1) * (m - i + 1), 0ll);
        if (i != 1) x1 *= 2ll;
        ll x2 = max(1ll * (n - i + 1) * (m - (i + 1) + 1), 0ll) +
                max(0ll, 1ll * (n - (i + 1) + 1) * (m - i + 1));
        // printf("%d:: %lld %lld\n",i,x1,x2);
        ans += x1 + x2;
    }
 
    rep(test, 1, q) {
        int x,y;
        read(x);read(y);
        work(x,y);
        write(ans);pc('\n');
    }
}

F - RBS

思路:我們定義$ "("$的權值為1, \(")"\)的權值為-1。我們定義合法字串\(s\)\(s\)的所有字首的權值和的最小值大於等於0。只有合法字串才能夠更新答案,否則該字串的答案就已經確定了,就沒必要再進行更新。

  • 例如:\((()))\)是一個非法字串,\((())(\)是一個合法字串。

考慮動態規劃,因為最多隻有20個串,所以我們可以用二進位制\(01\)來表示集合。設\(f[i]\)表示選擇集合\(i\)的最佳答案。我們再維護一個\(g\)陣列,表示在當前集合取最佳答案時的權值和為多少。特別的,如果為非法字串,那麼\(g[i] = -1\)。考慮轉移,根據一般動態規劃特性,我們應該列舉該集合中哪一個串是出現在最後。然後用這個來更新答案。我們假設列舉的串編號為\(t\) 那麼我們轉移的集合應該是\(i \bigoplus (1<<t)\),那麼分類討論:

  • \(g[i \bigoplus (1<<t)] = -1\),那麼說明已經是非法字串了,答案不會再增加,所以不用轉移。
  • \(g[i\bigoplus(1<<t)] + min[t] < 0\),注:這裡的\(min[t]\)是指字串\(t\)的最小字首和。那麼這表明,雖然\(1\bigoplus(1<<t)\)並不是非法字串,但是最後加入字串\(t\)之後,就變成了非法字串,那麼我們可以更新\(ans\) ,維護兩個桶,第一個桶簡單代表字首和的出現次數,第二個桶代表第一次出現比\(i\)小的字首和時,\(i\)的個數。我們這裡主要是用第二個桶來完成更新。因為我們已經知道該集合的權值和,那麼又知道了經過這一次更新,將變為非法字串,那麼在字串\(t\)中,肯定有一個位置,在這個位置之前,原串是合法字串,在這個位置之後,原串是非法字串,那我們第二個桶記錄\(i\)總是在第一個桶第一次記錄\(i-1\)時,就是表明在字首和最低降到\(i\)時,有多少個位置的字首和為\(i\)。我們不難寫出轉移方程\(sum = f[i\bigoplus(1<<t)] + S2[t][-g[i\bigoplus(1<<t)]]\)
  • \(g[1\bigoplus(1<<t] + min[t] \geq 0\) ,這說明經過這輪更新之後,依舊是合法字串,那麼我們可以更新該字串的\(f,g\)陣列,並且用第一個桶進行維護答案。

因為維護桶用的是\(unorderedmap\),所以複雜度\(O(n2^{20})\)

\(Code:\)

string a[33];
int n;
int mi[33], Sum[33];
int f[(1 << 21)], g[(1 << 21)];
unordered_map<int, int> S1[22], S2[22];
void solve() {
    ios::sync_with_stdio(false);
    cin >> n;
    rep(i, 0, n - 1) {
        cin >> a[i];
        int now = 0;
        for (auto j : a[i]) {
            if (j == '(')
                now++;
            else
                now--;
            mi[i] = min(mi[i], now);
            //記錄字首個數
            S1[i][now]++;
            //記錄第一次不能統計now+1時的數量
            if (S1[i][now] == 1) {
                S2[i][now + 1] = S1[i][now + 1];
            }
        }
        Sum[i] = now;
    }
    int ans = 0;
    for (int i = 1; i < (1 << n); ++i) {
        g[i] = -1;
        //列舉第j個做集合i的最後一個字串
        for (int j = 0; j < n; ++j) {
            if ((i >> j) & 1) {
                int id = i ^ (1 << j);
                if (g[id] < 0) {
                    continue;
                } else if (g[id] + mi[j] < 0) {
                    int sum = S2[j][-g[id]] + f[id];
                    ans = max(ans, sum);
                } else {
                    int res = S1[j][-g[id]] + f[id];
                    if (f[i] < res) {
                        f[i] = res;
                        
                    }g[i] = Sum[j] + g[id];
                    ans = max(ans,f[i]);
                }
            }
        }
    }
    write(ans);pc('\n');
}

The Sum of Good Numbers

思路:設\(A+B=X\)\(A \geq B\),那麼\(A,B\)的長度可以有以下幾種

  • \(|A| = |X|-1,|B| = |X|-1\)
  • \(|A| = |X|\)

那麼第一種情況,我們可以列舉在\(a\)串中所有長度為\(|X|-1\)的串來作為答案。重點考慮第二種情況,我們列舉所有長度為\(|X|\)的串為\(A\)來判斷,我們可以發現確定\(A,X\)之後,\(B\)的長度其實是由\(X\)\(A\)的最長公共字首決定的,因為沒有\(0\)的存在,所以\(|A|-lcp(A,X)_{len}\)即為\(B\)的長度,在確定\(B\)的長度以及\(B\)的位置後,我們可以取出\(B\)來進行判斷。\(B\)長度可以在\(|A|-lcp(A,X)_{len} \pm2\)的範圍內都試一下。

那麼我們取出\(A,B\)之後,接下來需要判斷\(A+B\)是否等於\(X\),高精度複雜度\(O(n^2)\)明顯不行,我們發現可以利用字串雜湊的方式\(O(1)\)的判斷是否合法。問題就解決了。

複雜度\(O(n)\)

(擺爛了,程式碼不寫了