• Codeforces Round #814 (Div. 2) A - F


    Codeforces Round #814 (Div. 2) A - F

    前言: 感觉这一场对算法的要求并不是特别高,但是对思维与代码实现的要求相对较高。


    A. Chip Game

    思路: 简单博弈,结论是起点与终点距离是奇数 Burenka 胜利,否则 Tonya 胜利。

    代码:

    #include
    using namespace std;
    #define int long long 
    #pragma GCC optimize(3)
    void cf(){
        int a,b;
        cin>>a>>b;
        if((a+b)%2==0)cout<<"Tonya"<<endl;
        else cout<<"Burenka"<<endl;
    }
    signed main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int _=1;
        cin>>_;
        while(_--){
            cf();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    B. Mathematical Circus

    题意: 给你一个 n n n 和一个 k k k n n n 是偶数,问是否能任意两两配对 1 − n 1-n 1n 构造出 n / 2 n/2 n/2 组数使得每组数中的 a a a b b b 满足 ( a + k ) ∗ b (a+k)*b (a+k)b 能被 4 4 4 整除。

    思路: n n n 个数中有 n / 2 n/2 n/2 个奇数和 n / 2 n/2 n/2 个偶数,如果 k k k 是奇数,那么我们可以将 n / 2 n/2 n/2 个奇数放置左端, n / 2 n/2 n/2 个偶数放置右端。每一组形成 e v e n even even ∗ * e v e n even even 一定能被 4 4 4 整除。如果 k k k 是偶数,分类讨论,假如 k k k % 4 = = 0 4==0 4==0 那么 k k k 对左端不会产生贡献, ⌊ n 4 ⌋ \left\lfloor\dfrac{n}{4}\right\rfloor 4n 4 4 4 的倍数的偶数可以带动 ⌊ n 4 ⌋ \left\lfloor\dfrac{n}{4}\right\rfloor 4n 个奇数,剩余 ⌊ n 4 ⌋ \left\lfloor\dfrac{n}{4}\right\rfloor 4n 个奇数无法被带动,因此无解。假如 k k k % 4 = = 2 4==2 4==2 ,那么在 k k k % 4 = = 0 4==0 4==0 的前提下,将剩余 ⌊ n 4 ⌋ \left\lfloor\dfrac{n}{4}\right\rfloor 4n 个非 4 4 4 倍数的偶数放置左端即可带动剩余 ⌊ n 4 ⌋ \left\lfloor\dfrac{n}{4}\right\rfloor 4n 个奇数。

    代码:

    #include
    using namespace std;
    #define int long long 
    #pragma GCC optimize(3)
    void cf(){
        int n,k;
        cin>>n>>k;
        k%=4;
        if(k==0){
            cout<<"NO"<<endl;
        }
        else if(k%4==2){
            cout<<"YES"<<endl;
            for(int i=n;i>=2;i-=2){
                if(i%4){
                    cout<<i<<' '<<i-1<<endl;
                }
                else cout<<i-1<<' '<<i<<endl;
            }
        }
        else{
            cout<<"YES"<<endl;
            for(int i=n;i>=2;i-=2){
                cout<<i-1<<' '<<i<<endl;
            }
        }
    }
    signed main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int _=1;
        cin>>_;
        while(_--){
            cf();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    C. Fighting Tournament

    题意: 给定一组序列 { a n } \{ a_n\} {an},表示有 n n n 个运动员,一开始他们按 i d id id 升序排好,接下去会进行无限制格斗,每次格斗由 a 1 a_1 a1 a 2 a_2 a2 战斗,赢的人留下当 a 1 a_1 a1 输的人滚到队尾当 a n a_n an 。接下去有 q q q 次查询,查询一个 i d id id 在前 k k k 个回合内胜利场数。

    思路: 这题算是一道一眼模拟题,很显然当最大的元素在经过若干回合之后到达队头,剩下的回合数无论怎么战斗都是 a 1 a_1 a1 胜利。最大元素到达队头的过程不会超过 1 0 5 10^5 105 个回合,我们可以直接模拟这个过程,有放到队头和放到队尾两个操作,所以我们可以用 d e q u e deque deque 这个数据结构来模拟这个过程。最后查询的时候分类讨论一下,套一个二分即可。

    代码:

    #include
    using namespace std;
    #define int long long 
    #pragma GCC optimize(3)
    typedef pair<int,int>PII;
    #define pb push_back
    const int N = 1e5+10;
    PII a[N];
    vector<int>v[N];
    void cf(){
        int n,q;
        cin>>n>>q;
        for(int i=1;i<=n;i++)v[i].clear();
        int ma=0;
        deque<PII>q2;
        for(int i=1;i<=n;i++){
            cin>>a[i].first;
            a[i].second=i;
            q2.push_back(a[i]);
            ma=max(ma,a[i].first);
        }
        int ti=0;//回合数
        while(1){
            if(q2.front().first==ma)break;
            ti++;
            PII x=q2.front();
            q2.pop_front();
            PII y=q2.front();
            q2.pop_front();
            if(x.first>y.first){
                q2.push_front(x);
                q2.push_back(y);
                v[x.second].pb(ti);
            }
            else if(x.first<y.first){
                q2.push_back(x);
                q2.push_front(y);
                v[y.second].pb(ti);
            }
        }
        int id=q2.front().second;//最大元素的id
        while(q--){
            int x,y;
            cin>>x>>y;
            if(x==id){    
                if(ti){//第二到第一的过程也胜利了一次
                    if(y<ti)cout<<0<<endl;
                    else cout<<y-ti+1<<endl;
                }
                else{//始终在第一位
                    cout<<y<<endl;
                }
            }
            else if(v[x].empty()){
                cout<<0<<endl;
            }
            else{
                int idx=upper_bound(v[x].begin(),v[x].end(),y)-v[x].begin();
                cout<<idx<<endl;
            }
        }
    }
    signed main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int _=1;
        cin>>_;
        while(_--){
            cf();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    D Burenka and Traditions

    题意: 给定一组序列 { a n } \{ a_n\} {an},每次可以花费 ⌈ r − l + 1 2 ⌉ \left\lceil\dfrac{r-l+1}{2}\right\rceil 2rl+1 的代价使得区间 [ l , r ] [l,r] [l,r] 内所有数 ⨁ \bigoplus x x x ( x 由自己设定 ) (x由自己设定) (x由自己设定) ,问最小的代价使得区间 [ 1 , n ] [1,n] [1,n] 中所有的数变成 0 0 0

    思路: 首先答案的上限肯定是 n n n 的,这毋庸置疑,因为我们可以对每个数单独处理,单独 ⨁ \bigoplus 上其本身,所需代价是 n n n。接下去我们思考如何继续降低花费的总代价,首先观察样例,以及 ⌈ r − l + 1 2 ⌉ \left\lceil\dfrac{r-l+1}{2}\right\rceil 2rl+1 一个重要的信息。第一个样例 5 5 5 5 5 5 5 5 5 5 5 5 显然可以直接对区间 [ 1 , 4 ] [1,4] [1,4] ⨁ \bigoplus 5 5 5 所需代价是 2 2 2 。但是 同样可以 对区间 [ 1 , 2 ] [1,2] [1,2] ⨁ \bigoplus 5 5 5 对区间 [ 3 , 4 ] [3,4] [3,4] ⨁ \bigoplus 5 5 5,所需代价为 1 + 1 = 2 1+1=2 1+1=2,同样为最小代价。第二个样例 1 1 1 3 3 3 2 2 2 为什么等于 2 2 2 呢?因为我们可以对区间 [ 1 , 2 ] [1,2] [1,2] ⨁ \bigoplus 1 1 1 ,对区间 [ 2 , 3 ] [2,3] [2,3] ⨁ \bigoplus 2 2 2 所需代价为 1 + 1 = 2 1+1=2 1+1=2还不是很明显 ,我们看第四个样例。 2 2 2 5 5 5 7 7 7,对区间 [ 1 , 2 ] [1,2] [1,2] ⨁ \bigoplus 2 2 2 , 对区间 [ 2 , 3 ] [2,3] [2,3] ⨁ \bigoplus 7 7 7 所需代价为 2 2 2。于是通过归纳,较为容易发现一个性质,如果前缀异或中出现过当前枚举的异或值时,那么该段区间的所需代价为 r − l r-l rl 就会比原来单独处理少掉 1 1 1 的代价。例如: 2 2 2 5 5 5 8 8 8 9 9 9 13 13 13 11 11 11 中, 2 2 2 ⨁ \bigoplus 5 5 5 ⨁ \bigoplus 8 8 8 ⨁ \bigoplus 9 9 9 ⨁ \bigoplus 13 13 13 ⨁ \bigoplus 11 = 0 11=0 11=0 ,而一个数不选的情况下 ⨁ \bigoplus 0 0 0 ,所以区间 2 2 2 5 5 5 8 8 8 9 9 9 13 13 13 11 11 11 的所需代价为 6 − 1 = 5 6-1=5 61=5。那么有了这个结论,我们只需从前往后扫描,用一个 s e t set set 进行不断存储,一旦出现 s e t set set 中出现过的数,那么意味的前面有一段区间是可以只花费 r − l r-l rl 的代价变为 0 0 0 。所以最终的做法就是用 n n n − - 可以降低代价的区间数量 可以降低代价的区间数量 可以降低代价的区间数量

    代码:

    #include
    using namespace std;
    #define int long long 
    #pragma GCC optimize(3)
    const int N = 1e5+10;
    int a[N];
    void cf(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++)cin>>a[i];
        set<int>s;
        s.insert(0);
        int ans=n;
        int res=0;
        for(int i=1;i<=n;i++){
            res^=a[i];
            if(s.count(res))ans--,s.clear();
            s.insert(res);
        }
        cout<<ans<<endl;
    }
    signed main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int _=1;
        cin>>_;
        while(_--){
            cf();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    E. Fibonacci Strings

    题意: 给定 k k k 种不同类别的字母及数量,问我们是否能将其构造为一组斐波那契数列,斐波那契数列满足性质为 : : : 1 1 1 1 1 1 2 2 2 3 3 3 5 5 5 8 8 8 13... 13... 13... ,例如: k k k 3 3 3 其数量分别为 3 3 3 1 1 1 3 3 3 如何构造斐波那契数列,假设有 3 3 3 a a a 1 1 1 b b b 3 3 3 c c c 。那么可以构造为 a b a a c c c abaaccc abaaccc 1 1 1 1 1 1 2 2 2 3 3 3

    思路: 那么很显然如果 k k k 个不同类别的字母数量之和不是斐波那契数列的其中一个前缀和,斐波那契数列前缀和为 : : : 1 1 1 2 2 2 4 4 4 7 7 7 12 12 12 20... 20... 20... 。例如 k k k 个不同类别的字母数量之和为 8 8 8 的情况,那么显然是无法构成的。我们继续观察,会发现一个很重要的性质,由于相邻两项都是同样字符组成的话,其属于同一个数,例如: a a aa aa a a a aaa aaa a a aa aa b b b bbb bbb,前者表示 5 5 5 后者表示 2 2 2 3 3 3 。那么通过推公式就可以发现, f i b [ i ] > f i b [ i − 1 ] + f i b [ i − 3 ] + f i b [ i − 5 ] + . . . fib[i]>fib[i-1]+fib[i-3]+fib[i-5]+... fib[i]>fib[i1]+fib[i3]+fib[i5]+... 例如 : : : 1 1 1 1 1 1 2 2 2 3 3 3 5 5 5 8 8 8 13 13 13 21... 21... 21... 21 > = 13 + 5 + 2 + 1 21>=13+5+2+1 21>=13+5+2+1这就意味着 如果这给定的 k k k 种不同类别的字母及数量能够构成斐波那契数列,那么每个 > = f i b [ i ] >=fib[i] >=fib[i] 的数必定要包含 f i b [ i ] fib[i] fib[i] 这一组值,并且若严格 > > > f i b [ i ] fib[i] fib[i] 的情况必须由该类别的字母充当 f i b [ i ] fib[i] fib[i] 否则再前面的话最多也只能提供 f i b [ i ] fib[i] fib[i] 即前面所提到的 21 > = 13 + 5 + 2 + 1 21>=13+5+2+1 21>=13+5+2+1 ,那么 必然会有剩余部分残留且无法再使用 。所以综上得出本题做法 : : : 首先判断 k k k 种不同类别的字母的数量和是否为斐波那契数列的前缀,其次不断从斐波那契的高位向低位填充,每次贪心用 目前最大数量的种类 填充,可以用 优先队列 存储,如果遇到非法情况直接直接输出 NO 结束 s o l v e solve solve ,否则最后输出 YES

    代码:

    #include
    using namespace std;
    #define int long long 
    #pragma GCC optimize(3)
    typedef pair<int,int>PII;
    const int N = 110;
    int fib[N];
    int ok;
    map<int,int>mp;
    int n;
    void init(){
        fib[1]=fib[2]=1;
        for(int i=3;i<=50;i++){
            fib[i]=fib[i-1]+fib[i-2];
        }
        int sum=0;
        for(int i=1;i<=50;i++){
            sum+=fib[i];
            mp[sum]=i;
        }
    }
    void cf(){
        ok=-1;
        cin>>n;
        int sum=0;
        priority_queue<PII>heap;
        for(int i=1;i<=n;i++){
            int x;
            cin>>x;
            sum+=x;
            heap.push({x,i});
        }
        if(!mp[sum])cout<<"NO"<<endl;
        else{
            int idx=mp[sum];
            for(int i=idx;i>=1;i--){
                auto x=heap.top();
                heap.pop();
                if(x.first<fib[i]){
                    cout<<"NO"<<endl;
                    return ;
                }
                else if(ok==x.second){
                    if(heap.empty()){
                        cout<<"NO"<<endl;
                        return ;
                    }
                    else{
                        if(heap.top().first>=fib[i]){
                            auto y=heap.top();
                            heap.pop();
                            ok=y.second;
                            y.first-=fib[i];
                            heap.push(x);
                            heap.push(y);
                        }
                        else{
                            cout<<"NO"<<endl;
                            return ;
                        }
                    }
                }
                else{
                    x.first-=fib[i];
                    ok=x.second;
                    heap.push(x);
                }
            }
            cout<<"YES"<<endl;
        }
    }
    signed main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int _=1;
        cin>>_;
        init();
        while(_--){
            cf();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    F. Tonya and Burenka-179

    题意: 选定机器人的出发点,设定一个步长 k k k ( 1 < = k < n ) (1<=k1<=k<n , 机器人在其接下来的 n n n 步中每次向右移动 k k k 步,如果超出 n n n 号点 那么回到 1 1 1 号点,并在每一步中获得当前位置 i i i a i a_i ai 的值。问机器人 n n n 次移动能获得的所有值之和最大是多大。

    思路: 如果步长选择 2 2 2 或选择 4 4 4 会有什么不同?假如一组数有八个数组成分别为 a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3 a 4 a_4 a4 a 5 a_5 a5 a 6 a_6 a6 a 7 a_7 a7 a 8 a_8 a8,如果我们步长选择 2 2 2 ,那么有路径 a 1 a_1 a1 a 3 a_3 a3 a 5 a_5 a5 a 7 a_7 a7 a 1 a_1 a1 a 3 a_3 a3 a 5 a_5 a5 a 7 a_7 a7 a 2 a_2 a2 a 4 a_4 a4 a 6 a_6 a6 a 8 a_8 a8 a 2 a_2 a2 a 4 a_4 a4 a 6 a_6 a6 a 8 a_8 a8…等情况,如果我们步长选择 4 4 4 ,那么有路径 a 1 a_1 a1 a 5 a_5 a5 a 1 a_1 a1 a 5 a_5 a5 a 1 a_1 a1 a 5 a_5 a5 a 1 a_1 a1 a 5 a_5 a5 a 2 a_2 a2 a 6 a_6 a6 a 2 a_2 a2 a 6 a_6 a6 a 2 a_2 a2 a 6 a_6 a6 a 2 a_2 a2 a 6 a_6 a6…,此处有一个很重要的性质,由于 我们希望 n n n 次移动能获得的所有值之和最大,这就意味着 n n n 次移动的平均获得能最大,那么步长 2 2 2 和步长 4 4 4 的区别是什么呢?步长 4 4 4 会作用于 2 2 2 个数,步长 2 2 2 会作用于 4 4 4 个数, 4 4 4 个数比 2 2 2 个数更能拉低平均值,什么意思呢?例如有 2 2 2 3 3 3 7 7 7 8 8 8 9 9 9 5 5 5 11 11 11 6 6 6 这八个数,最好的选法肯定是 ( a 3 + a 7 ) ∗ 4 = 72 (a_3+a_7)*4=72 (a3+a7)4=72,而步长为 2 2 2 ,即选择四个数必然无法 72 72 72 ,因为在包含 a 3 a_3 a3 a 7 a_7 a7 的路径上还存在 a 5 a_5 a5 a 1 a_1 a1 其值之和 11 < 18 11<18 11<18 ,固拉低了平均值。因此我们可以得出,如果存在能够产生作用的多个数,例如产生作用 8 、 9 8、9 89 ,那么选择产生作用 2 、 3 2、3 23个数,即步长为 n / 2 、 n / 3 n/2、n/3 n/2n/3必然能使得答案最优 。综上我们只需枚举一个数所有的质因子即可,一个数范围在 2 e 5 2e5 2e5 内因子数最大不超过 6 6 6 ,因为 2 ∗ 3 ∗ 5 ∗ 7 ∗ 11 ∗ 13 ∗ 17 > 2 e 5 2*3*5*7*11*13*17>2e5 2357111317>2e5。剩余的就是模拟即可。

    代码:

    #include
    using namespace std;
    #define int long long 
    #pragma GCC optimize(3)
    int n,q;
    const int N = 2e5+10;
    int a[N];
    int b[N];
    int s[10][N];
    int tot;
    void cf(){
        cin>>n>>q;
        tot=0;
        for(int i=0;i<n;i++)cin>>a[i];
        int x=n;
        for(int i=2;i<=x;i++){
            if(x%i==0){
                while(x%i==0){
                    x/=i;
                }
                b[++tot]=n/i;//多少个数
                for(int j=0;j<n;j++){
                    s[tot][j%b[tot]]+=a[j];
                }
            }
        }
        multiset<int>se;
        for(int i=1;i<=tot;i++){
            for(int j=0;j<b[i];j++)se.insert(s[i][j]*b[i]);
        }
        cout<<*se.rbegin()<<endl;
        while(q--){
            int x,y;
            cin>>x>>y;
            x--;//下标从0开始
            for(int i=1;i<=tot;i++){//替换
                se.erase(se.find(s[i][x%b[i]]*b[i]));//如果找不到的话会报错,一定要是se中有该数的情况,该题能保证有该数。
                s[i][x%b[i]]+=y-a[x];
                se.insert(s[i][x%b[i]]*b[i]);
            }
            a[x]=y;
            cout<<*se.rbegin()<<endl;
        }
        for(int i=1;i<=tot;i++){
            for(int j=0;j<b[i];j++)s[i][j]=0;
        }
    }
    signed main(){
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
        int _=1;
        cin>>_;
        while(_--){
            cf();
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
  • 相关阅读:
    whisper 语音识别项目部署
    【SpringBoot】当AOP引发的异常与@RestControllerAdvice擦肩而过:异常处理的盲点揭秘
    What is a UDP Flood Attack?
    第十一周周报
    基于TensorFlow 2.3.0 的手势识别系统设计
    gitee使用教程
    智能水电表对于普通居民来说有哪些好处?
    JWT详解(文章内嵌jwt工具类)
    手把手教你如何通过CC2531抓取Zigbee包,并解析加密Zigbee包
    人工智能序章:开发环境搭建Anaconda+VsCode+JupyterNotebook(零基础启动)
  • 原文地址:https://blog.csdn.net/qq_53504307/article/details/126393154