Codeforces Global Round 26 補題記錄(A~C2)

yhbqwq發表於2024-06-10

下大分場/ll

A

\(a\) 的值全部相同則無解,否則:

  • 存在一個數出現了至少兩次。那麼讓其中的恰好一次染上紅色,其他的數全部染上藍色。
  • 不存在一個數出現了至少兩次。那麼隨機選取一個數染上紅色,其他的數全部染上藍色。

時間複雜度為 \(O(n)\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N];
signed main() {
    int T;
    cin>>T;
    while(T--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        if(a[1]==a[n])cout<<"NO\n";
        else{
            cout<<"YES\n";
            map<int,int>mp;
            for(int i=1;i<=n;i++)mp[a[i]]++;
            for(int i=1;i<=n;i++)if(mp[a[i]]>=2){
                for(int j=1;j<=n;j++)if(j==i)cout<<"R";else cout<<"B";
                cout<<'\n';
                goto ee;
            }
            cout<<"R";
            for(int i=2;i<=n;i++)cout<<"B";
            cout<<'\n';
            ee:;
        }
    }
}

B

考慮從低位到高位模擬過程。如果在列舉到某一位的值的時候,值為 \(9\),那麼因為 \(9\)\(19\) 均不可以被分成兩個 \(5\sim 9\) 的數的和,所以必然無解。否則必然可以分成兩個 \(5\sim 9\) 的數的和。設這個位置的值為 \(x\),那麼會讓答案減去 \(10+x\)。如果減到一個位置的時候,答案不夠減,那麼就必然存在無解。如果列舉到第一位的時候剩下的值仍然不為 \(0\),那麼無解(沒有地方可以退位),否則有解。

時間複雜度為 \(O(|n|)\),其中 \(|n|\) 重定義為 \(n\)\(10\) 進位制下的位數。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
char s[N];
int a[N];
signed main() {
    int T;
    cin>>T;
    while(T--){
        scanf("%s",s+1);
        bool ok=true;
        int n=strlen(s+1);
        for(int i=1;i<=n;i++)a[i]=s[i]^48;
        a[0]=a[n+1]=a[n+2]=a[n+3]=a[n+4]=a[n+5]=0;
        for(int i=n;i>1;i--){
            if(a[i]==9){ok=false;break;}
            a[i-1]--;
            for(int j=i-1;j;j--){
                if(a[j]>=0)break;
                a[j-1]--,a[j]+=10;
            }
            if(a[0]<0){ok=false;break;}
        }
        cout<<((ok&&!a[1])?"YES":"NO")<<'\n';
    }
}

C1

發現要麼一直不用 \(2\) 操作,要麼在答案取到最小值的時候使用一遍 \(2\) 操作讓其變為相對大一些的值。顯然使用多遍 \(2\) 操作是不優的。然後還能發現若存在一個位置 \(p\) 使得字首和 \(<0\),那麼這個地方不用 \(2\) 操作肯定不如用 \(2\) 操作優。因此若不存在一個字首和的值 \(<0\) 那麼就不應該使用 \(2\) 操作。否則一定在任意一個值最小的位置使用 \(2\) 操作即可。

時間複雜度為 \(O(n)\)

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N],pre[N];
signed main() {
    int T;
    cin>>T;
    while(T--){
        int n,id=-1;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]+a[i];
        int mi=0;
        for(int i=n;i;i--)if(pre[i]<mi){
            mi=pre[i];
            id=i;
            // break;
        }
        if(id==-1)cout<<pre[n]<<'\n';
        else{
            int s=0;
            for(int i=1;i<=n;i++){
                if(i<=id)s-=a[i];
                else s+=a[i];
            }
            cout<<s<<'\n';
        }
    }
}

C2

不會猜結論。每一步結束之後,若當前的字首和取到了全部字首和的最小值,則這一步對答案造成了 \(2^{n-i+r}\),其中 \(r\) 是在這一步操作中字首和 \(\ge0\) 的位置的數量。若全部操作中沒有任何一個字首和 \(<0\) 則答案為 \(2^n\)。考慮這個為什麼是對的。

  • 不存在任何一個字首和使得這個字首和 \(\le 0\)。此時對於任意一個位置 \(1\) 操作和 \(2\) 操作沒有任何本質區別。任意一步都可以在 \(1\) 操作和 \(2\) 操作中任選一個操作執行。
  • 存在一個字首和使得這個字首和 \(\le0\)。因為執行完這一步之後後面不論怎麼走答案都不會偏離最大值(參見 C1 的結論),所以說後面的任意一步都可以在 \(1\) 操作和 \(2\) 操作中任選,即 \(2^{n-i}\) 種不同選擇;若前面有字首和 \(\ge 0\) 的那麼 \(1\) 操作和 \(2\) 操作沒有本質的區別,若有 \(r\) 個滿足這樣條件的則有 \(2^r\) 種不同選擇。根據乘法原理可知對答案的貢獻為 \(2^{n-i+r}\)。其中 \(r\) 可以在列舉的時候掃一遍得出答案。

時間複雜度為 \(O(n)\)

Brute Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100;
int a[N],pre[N];
signed main() {
    int T;
    cin>>T;
    while(T--){
        int n,id=-1;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]+a[i];
        int mi=0;
        for(int i=n;i;i--)if(pre[i]<mi){
            mi=pre[i];
            id=i;
            // break;
        }
        if(id==-1){
            cout<<pre[n]<<'\n';
            int s=pre[n];
            int cnt=0;
            for(int i=0;i<(1ll<<n);i++){
                int now=0;
                for(int j=1;j<=n;j++)if(i>>(j-1)&1)now+=a[j];
                else now=abs(now+a[j]);
                if (now==s)cnt++;
            }
            cout<<cnt<<'\n';
        }
        else{
            int s=0;
            for(int i=1;i<=n;i++){
                if(i<=id)s-=a[i];
                else s+=a[i];
            }
            cout<<s<<'\n';
            int cnt=0;
            for(int i=0;i<(1ll<<n);i++){
                int now=0;
                for(int j=1;j<=n;j++)if(i>>(j-1)&1)now+=a[j];
                else now=abs(now+a[j]);
                if (now==s)cnt++;
            }
            cout<<cnt<<'\n';
        }
    }
}

Correct Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000100,mod=998244353;
int a[N],pre[N],bin[N];
signed main() {
    int T;
    cin>>T;
    bin[0]=1;
    for(int i=1;i<N;i++)bin[i]=bin[i-1]*2%mod;
    while(T--){
        int n,id=-1;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i],pre[i]=pre[i-1]+a[i];
        int mi=0,r=0;
        for(int i=1;i<=n;i++)mi=min(mi,pre[i]);
        if(mi==0)cout<<bin[n]<<'\n';
        else{
            int ans=0;
            for(int i=1;i<=n;i++){
                if(pre[i]>=0)r++;
                if(pre[i]==mi)ans=(ans+bin[n-i+r])%mod;
            }
            cout<<ans<<'\n';
        }
    }
}

相關文章