1. 程式人生 > 其它 >Codeforces Round #697 (Div. 3)A-G題解

Codeforces Round #697 (Div. 3)A-G題解

技術標籤:演算法入門

A. Odd Divisor

題目連結:點選此處

每個大於等於2的整數都可以劃分為質數的積,然後質數只有2是偶數。所以我們對於一個數除完2,看看是否為1,為1就NO,大於1就是YES。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include
<queue>
using namespace std; #define Check(x) cout<<"x:"<<x<<" " #define Min(x,y,z) min(x,min(z,y)) #define Max(x,y,z) max(x,max(z,y)) typedef long long ll; const int MAXN = 1e5 + 5; const int MAXM = 1e6 + 5; const ll mod = 1e9 + 7; ll n, m; ll arr[MAXN]; ll sum[
MAXN]; int main() { int t; cin >> t; while (t--) { cin >> n; while (n % 2 == 0) { n /= 2; } if (n == 1) { cout << "NO" << endl; } else cout << "YES" << endl; } }

B. New Year’s Number

題目連結:點選此處

觀察一個數是否被 2020 2020 2020 2021 2021 2021組成,很明顯的是 2021 = 2020 + 1 2021=2020+1 2021=2020+1,所以如果這個數 a i a_i ai符合要求,那麼 a i = 2020 ∗ x + 2021 ∗ y = 2020 ∗ ( x + y ) + y a_i=2020*x+2021*y=2020*(x+y)+y ai=2020x+2021y=2020(x+y)+y,所以取餘2020,會得到 y y y,然後 a i a_i ai減去 2021 ∗ y 2021*y 2021y,觀察是否大於等於 0 0 0,並且能被 2020 2020 2020取餘,能的話YES,不能NO。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
ll sum[MAXN];
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n;
        ll a = n % 2020;
        ll sum = n - a * 2021;
        if (sum < 0 || sum % 2020 != 0) {
            cout << "NO" << endl;
        }
        else cout << "YES" << endl;
    }

}

C. Ball in Berlan

題目連結:點選此處

給我n個數,那麼我們可以得到 ( n − 1 ) ∗ n 2 \frac{(n-1)*n}{2} 2(n1)n組,但是我們要減去不符合的。即出現重複的。這裡我們要知道一個性質。如果 a i a_i ai a j a_j aj重複,那麼 b i b_i bi b j b_j bj不會重複,因為如果重複,那麼就是同一組了,題意不允許出現相同兩組。

所以我們遍歷陣列到 a i a_i ai時,假設前面有 k 1 k_1 k1個與 a i a_i ai重複, k 2 k_2 k2個與 b i b_i bi重複,那麼 a n s − = k 1 + k 2 ans-=k_1+k_2 ans=k1+k2。因為這些組不能和 a i , b i {a_i,b_i} ai,bi組一起。對於 a i , b i {a_i,b_i} ai,bi後續的組,如果出現與 a i , b i {a_i,b_i} ai,bi重複,那麼通過後續的組來刪去。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll a[MAXN], b[MAXN];
ll a1[MAXN] = { 0 }, b1[MAXN] = { 0 };
ll arr[MAXN]; 
int main() {
    int t;
    cin >> t;
    while (t--) {
        ll k;
        cin >> n >> m >> k;
        ll ans = (k - 1) * k / 2;
        for (int i = 1;i <= k;i++) {
            cin >> a[i];
            ans -= a1[a[i]];
            a1[a[i]]++;
        }
        for (int i = 1;i <= k;i++) {
            cin >> b[i];
            ans -= b1[b[i]];
            b1[b[i]]++;
        }
        for (int i = 1;i <= k;i++) {
            a1[a[i]] = 0;
            b1[b[i]] = 0;
        }
        cout << ans << endl;
        
    }

}

D. Cleaning the Phone

題目連結:點選此處

c 1 [ i ] c1[i] c1[i]為第 i i i大的代價為1的容量。

c 2 [ i ] c2[i] c2[i]為第 i i i大的代價為2的容量。

d 1 [ i ] d1[i] d1[i]表示花費 i i i代價得到的最大容量後,選了幾個代價為1的。

d 2 [ i ] d2[i] d2[i]表示花費 i i i代價得到的最大容量後,選了幾個代價為2的。

我用的是DP, d p [ i ] dp[i] dp[i]表示花費了 i i i代價,最多能消除多少體積。那麼題目轉化很簡單, d p [ i ] = m a x ( d p [ i − 1 ] + c 1 [ d 1 [ i − 1 ] ] , d p [ i − 2 ] + c 2 [ d 2 [ i − 2 ] ] , d p [ i − 2 ] + c 1 [ d 1 [ i − 2 ] ] + c 1 [ d 1 [ i − 2 ] + 1 ] ) dp[i]=max(dp[i-1]+c1[d1[i-1]],dp[i-2]+c2[d2[i-2]],dp[i-2]+c1[d1[i-2]]+c1[d1[i-2]+1]) dp[i]=max(dp[i1]+c1[d1[i1]],dp[i2]+c2[d2[i2]],dp[i2]+c1[d1[i2]]+c1[d1[i2]+1]),即現在花費 i i i的最大值只能由花費 i − 2 i-2 i2的加上一個代價2的或者兩個代價1的,或者花費 i − 1 i-1 i1的加上一個代價1的。之後得到相應最大值後,把 d 1 [ i ] d1[i] d1[i] d 2 [ i ] d2[i] d2[i]隨之更新一下就行。

#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 5e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll a[MAXN], b[MAXN];
ll c1[MAXN], c2[MAXN];
ll dp[MAXN];
ll d1[MAXN], d2[MAXN];
bool cmp(ll& a, ll& b) {
    return a > b;
}
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        int cnt1 = 0, cnt2 = 0;
        int sum = 0;
        for (int i = 1;i <= n;i++)cin >> a[i];
        for (int i = 1;i <= n;i++) { 
            cin >> b[i]; 
            sum += b[i];
            if (b[i] == 1)c1[++cnt1] = a[i];
            else c2[++cnt2] = a[i];
        }
        sort(c1 + 1, c1 + 1 + cnt1,cmp);
        sort(c2 + 1, c2 + 1 + cnt2, cmp);
        d1[0] = d2[0] = 1;
        dp[0] = 0;
        dp[1] = c1[1];
        d1[1] = 2;d2[1] = 1;
        if (dp[1] >= m) { cout << 1 << endl;
        //初始化
        d1[0] = d2[0] = dp[0] = dp[1] = d1[1] = d2[1] = 0;
        for (int i = 1;i <= cnt1;i++)c1[i] = 0;
        for (int i = 1;i <= cnt2;i++)c2[i] = 0;
        continue;
        }
        bool f = false;
        for (int i = 2;i <= sum;i++) {
            int k = 0;
            int tmp = 0;
            if (c1[d1[i - 2]] + c1[d1[i - 2] + 1] > c2[d2[i - 2]]) {
                tmp = c1[d1[i - 2]] + c1[d1[i - 2] + 1];
                dp[i] = dp[i - 2] + tmp;
                d1[i] = d1[i - 2] + 2;
                d2[i] = d2[i - 1];
            }
            else {
                tmp = c2[d2[i - 2]];
                dp[i] = dp[i - 2] + tmp;
                d2[i] = d2[i - 2] + 1;
                d1[i] = d1[i - 2];
            }
            if (tmp + dp[i - 2] < dp[i - 1] + c1[d1[i - 1]]) {
                tmp = c1[d1[i - 1]];
                dp[i] = dp[i - 2] + tmp;
                d1[i] = d1[i - 1] + 1;
                d2[i] = d2[i - 1];
            }
            if (dp[i] >= m) { cout << i << endl;f = true;break; }
           
        }
        //初始化
        for (int i = 1;i <= cnt1;i++)c1[i] = 0;
        for (int i = 1;i <= cnt2;i++)c2[i] = 0;
        for (int i = 0;i <= sum;i++)dp[i] = d1[i] = d2[i] = 0;
        if (!f)cout << -1 << endl;
        
    }

}

E. Advertising Agency

題目連結:點選此處

貪心,所以先對陣列排序,肯定要拿前k個。情況很多是因為有很多 a r r [ i ] arr[i] arr[i]可能是等於 a r r [ k ] arr[k] arr[k]的,那麼我們定一個 p r e pre pre l a s t last last標記,表示第一個值等於 a r r [ k ] arr[k] arr[k]的下標,和最後一個等於 a r r [ k ] arr[k] arr[k]的下標,然後 l a s t − p r e + 1 last-pre+1 lastpre+1得到 a r r [ k ] arr[k] arr[k]有幾個。之後就是組合數+逆元求解。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e3 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
ll arr[MAXN];
bool cmp(ll& a, ll& b) {
    return a > b;
}
void extend_gcd(ll a, ll b, ll& x, ll& y) {
    if (b == 0) {
        x = 1, y = 0;
        return;
    }
    extend_gcd(b, a % b, x, y);
    ll tmp = x;
    x = y;
    y = tmp - (a / b) * y;
}
//逆元
ll mod_inverse(ll a, ll b) {
    ll x, y;
    extend_gcd(a, b, x, y);
    //防止負數
    return (x % b + b) % b;
}
int main() {
    int t;
    cin >> t;
    while (t--) {
        cin >> n >> m;
        for (int i = 1;i <= n;i++)cin >> arr[i];
        sort(arr + 1, arr + 1 + n,cmp);
        int pre = MAXN;
        int last = 0;
        for (int i = 1;i <= n;i++) {
            if (arr[i] == arr[m]) {
                pre = min(pre, i);
                last = max(last, i);
            }
        }
        ll a = last - pre + 1;
        ll b = m - pre + 1;
        ll ans = 1;
        for (int i = a;i >= b + 1;i--)ans = ans * i % mod;
        for (int i = 1;i <= a - b;i++) {
            ans = ans * mod_inverse(i, mod)%mod;
        }
        cout << ans << endl;
    }

}

F. Unusual Matrix

題目連結:點選此處

很容易知道一行或者一列異或後,會使得所有值翻轉。

一個數翻轉2次等於沒動。

我們列舉矩陣,當列舉到 i , j i,j i,j時,前面列舉到的點都已經匹配完了。如果在該點需要翻轉,那麼我們不能列翻轉,因為翻轉列,會使得該列上面幾行的點匹配不成功,所以我們只能翻轉行,但是翻轉行又有問題了,因為該行前面的點已經匹配完了。翻轉行會使得前面的點不匹配。有人說,那麼在下一行我們翻轉列就行,但是我們這樣會使得該列最上面那些匹配好的點不匹配,形成矛盾。

所以我們瞭解到,在一定程度後,我們就不能再翻轉了,如果出現沒匹配的,那麼答案肯定是匹配不了的。

那麼我們要知道什麼條件才不能翻轉。

那就是出現後效性,即我們翻轉這個會使得我們前面匹配完的還要翻轉就不行。

那麼哪些點沒有後效性呢?就是第一列的所有點的行翻轉無後效性。第一行的所有點的列翻轉無後效性。

所以答案很顯然了,我們對第一行和第一列進行行列翻轉讓他們滿足要求,之後遍歷其他點,如果出現不符合的就是錯的。

程式碼中 m p [ i ] [ j ] = = 1 mp[i][j]==1 mp[i][j]==1表示要翻轉的,為0表示不用翻轉, h o r [ j ] hor[j] hor[j] v e r [ i ] ver[i] ver[i]表示第j行,第 i i i列是否翻轉過。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 1e3 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
int mp[MAXN][MAXN];
int ver[MAXN];
int hor[MAXN];
int main() {
    int t;
    cin >> t;
    while (t--) {
        int n;
        cin >> n;
        for (int i = 1;i <= n;i++) {
            for (int j = 1;j <= n;j++) {
                char ch;
                cin >> ch;
                mp[i][j] = ch - '0';
            }
        }
        for (int i = 1;i <= n;i++) {
            for (int j = 1;j <= n;j++) {
                char ch;
                cin >> ch;
                mp[i][j] = (mp[i][j] + (ch - '0')) % 2;
            }
        }
        if (n == 1) {
            cout << "YES" << endl;
            continue;
        }
        bool f = true;
        for (int i = 1;i <= n;i++) {
            if (mp[1][i] == 1) {
                hor[i] = 1;
            }
        }
        for (int i = 2;i <= n;i++) {
            if (hor[1])mp[i][1] = mp[i][1] ^ 1;
            if (mp[i][1] == 1)ver[i] = 1;
            else ver[i] = 0;
            for (int j = 2;j <= n;j++) {
                for (int k = 1;k <= ver[i] + hor[j];k++) {
                    mp[i][j] = mp[i][j] ^ 1;
                }
                if (mp[i][j] == 1) { f = false;break; }
            }
            if (!f)break;
        }
        for (int i = 1;i <= n;i++) {
            ver[i] = hor[i] = 0;
        }
        if (f)cout << "YES" << endl;
        else cout << "NO" << endl;
    }

}

G. Strange Beauty

題目連結:點選此處

這題我們要知道如果 a a a能被 b b b整除, b b b能被 c c c整除,那麼 a a a能被 c c c整除。

那麼我們很容易想到遞推。

d p [ b ] + = d p [ a ] dp[b]+=dp[a] dp[b]+=dp[a], d p [ c ] + = d p [ b ] dp[c]+=dp[b] dp[c]+=dp[b]

之後我們想列舉。發現列舉肯定會超時。

所以需要優化。 那麼怎麼才能知道一個數的整數倍有哪些呢?

我們知道一個數是由質數夠成的,所以我們只要列舉質數就行。

所以 d p [ i ] = d p [ i ] + m a x ( d p [ i ∗ p r i m e [ 1 ] ] , d p [ i ∗ p r i m e [ 2 ] ] , . . . d p [ i ∗ p r i m e [ c n t ] ] ) dp[i]=dp[i]+max(dp[i*prime[1]],dp[i*prime[2]],...dp[i*prime[cnt]]) dp[i]=dp[i]+max(dp[iprime[1]],dp[iprime[2]],...dp[iprime[cnt]])

通過這個我們知道我們要從後往前列舉。其中 i i i就表示一個數 i i i,不是表示 a r r [ i ] arr[i] arr[i],如果表示 a r r [ i ] arr[i] arr[i]就會錯誤,比如樣例 2 2 8,我們得到8的 d p dp dp值,但是不能通過 p r i m e prime prime得到2的dp值。只能通過4來作為間接值來得到,即 d p [ 4 ] = d p [ 4 ] + m a x ( d p [ 4 ∗ 2 ] , d p [ 4 ∗ 3 ] . . . ) , d p [ 2 ] = d p [ 2 ] + m a x ( d p [ 2 ∗ 2 ] , d p [ 2 ∗ 3 ] . . . ) dp[4]=dp[4]+max(dp[4*2],dp[4*3]...),dp[2]=dp[2]+max(dp[2*2],dp[2*3]...) dp[4]=dp[4]+max(dp[42],dp[43]...),dp[2]=dp[2]+max(dp[22],dp[23]...)

但是這樣列舉肯定複雜度很大,所以要優化,那麼就是 i ∗ p r i m e [ j ] i*prime[j] iprime[j]大於2e5時,我們就 b r e a k break break,這樣,我們會減少很多複雜度。

這邊我們初始化 d p [ i ] dp[i] dp[i]表示在 a r r arr arr中的出現次數。


#include<iostream>
#include<vector>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iomanip>
#include<stdio.h>
#include<map>
#include<queue>
using namespace std;
#define Check(x) cout<<"x:"<<x<<" "
#define Min(x,y,z) min(x,min(z,y))
#define Max(x,y,z) max(x,max(z,y))
typedef long long ll;
const int MAXN = 2e5 + 5;
const int MAXM = 1e6 + 5;
const ll mod = 1e9 + 7;
ll n, m;
int dp[MAXN];
int arr[MAXN];
bool visit[MAXN] = { 0 };
int prime[MAXN];
int cnt = 0;
//尤拉篩
void init() {
    for (int i = 2;i < MAXN;i++) {
        if (!visit[i])
            prime[++cnt] = i;
        for (int j = 1;j <= cnt, prime[j] * i < MAXN;j++) {
            visit[prime[j] * i] = true;
            if (i % prime[j] == 0)break;
        }
    }
}
int main() {
    int t;
    cin >> t;
    init();
    while (t--) {
        int n;
        cin >> n;
        int ans = 1;
        for (int i = 1;i <= n;i++) {
            cin >> arr[i];
            dp[arr[i]]++;
        }
        for (int i = 2e5;i >= 1;i--) {
            int maxn = 0;
            for (int j = 1;j <= cnt;j++) {
                if (i * prime[j] > (int)2e5)break;
                if (dp[i * prime[j]] != 0) {
                    maxn = max(maxn, dp[i * prime[j]]);
                }
            }
            dp[i] += maxn;
            ans = max(dp[i], ans);
        }
        for (int i = 1;i <= 2e5;i++) {
            dp[i] = 0;
        }
        cout << n - ans << endl;
    }

}