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

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

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


A. Chip Game

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

代码:

#include<bits/stdc++.h>
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;
}

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<bits/stdc++.h>
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;
}

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<bits/stdc++.h>
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;
}

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<bits/stdc++.h>
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;
}

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<bits/stdc++.h>
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;
}

F. Tonya and Burenka-179

题意: 选定机器人的出发点,设定一个步长 k k k ( 1 < = k < n ) (1<=k<n) 1<=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<bits/stdc++.h>
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值