Codeforces Round 929 (Div. 3)(A~E)

A. Turtle Puzzle: Rearrange and Negate

对数组求和,负数当正数来用,输出和。

#include <bits/stdc++.h>
//#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define fr first
#define se second
#define endl '\n'
using namespace std;

int n,ans,tmp;

void solve(){
    cin>>n;
    per(i,1,n){
        cin>>tmp;
        ans+=tmp>0?tmp:-tmp;
    }
    cout<<ans<<endl;
}

void init(){
    ans=0;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t;
    cin>>t;
    while(t--)solve(),init();
    return 0;
}

B. Turtle Math: Fast Three Task

你可以进行下列两个操作无限次。

1、选择一个数,删掉他,数组总长度-1(没数的时候不能用)

2、选择一个数,加1

问最少多少次操作,可以让数组的和是3的倍数(或者是0,有特殊情况)

先正常的求和,对3取模之后会有三种情况:0,1,2。

如果是0:那么不需要操作就可以得到答案,输出0。

如果是1:最少需要2步,即让某个数加两次1。考虑能不能有更优解,即1步。找到一个数,对3余数为1,存在的话就只需要1步。

如果是2:最少需要1步,操作1和操作2不用想了,反正都是1步。输出1就行

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define fr first
#define se second
#define endl '\n'
using namespace std;
const int N=1e5+5;

int n,a[N],sum;

void solve(){
    cin>>n;
    per(i,1,n)cin>>a[i],sum+=a[i];

    int leave=sum%3;
    if(leave==0){
        cout<<0<<endl;
    }else if(leave==1){
        //最少2步 有没有可能1步完成?,删除一个数,且对3的余数是2
        //只有一个的特殊情况
        if(n==1){
            cout<<1<<endl;
            return;
        }
        per(i,1,n)if(a[i]%3==1){
            cout<<1<<endl;
            return;
        }
        cout<<2<<endl;
    }else if(leave==2){
        //最少1步,不可能成为0步
        cout<<1<<endl;
    }
}

void init(){
    sum=0;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t;
    cin>>t;
    while(t--)solve(),init();
    return 0;
}

C. Turtle Fingers: Count the Values of k

l,a,b已知,x,y任选,问满足条件的k有几个。

因为是求k,所以改一下式子。

题目说了x和y是正整数或者0。所以下面就是一堆a*a*a*a*a*b*b*b*b,任意的a和b。

那么如果这个式子能整除,k就是一个合法的值。a和b的范围都是2~100,2^32次轻轻松松达到整数上限,超过了题目 l 的大小,所以我们找一个不超范围太多但是令人放心的大小即可,比如x和y都枚举到100为止。

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define fr first
#define se second
#define endl '\n'
using namespace std;

int a,b,l;

set<int>s;
map<int,int>f,g;

void solve(){
    cin>>a>>b>>l;

    int num=1,base=a;
    f[num]=a;//记录一下 a*a*a*a的值
    f[0]=1;
    while(a<=l){
        num++;
        a*=base;
        f[num]=a;
    }

    num=1,base=b;
    g[num]=b;//记录一下 b*b*b*b的值
    g[0]=1;
    while(b<=l){//小于等于l就行了,再大就会得到小数,题目中k是整数
        num++;
        b*=base;
        g[num]=b;
    }

    per(i,0,100){
        per(j,0,100){
            if(f[i] and g[j]){//如果存在的话就算一下是不是能完整除
                if(l%(f[i]*g[j])==0){
                    s.insert(l/(f[i]*g[j]));//防止重复,把k的值放到set里面,最后输出set的大小
                }
            }else break;
        }
    }

    cout<<s.size()<<endl;
}

void init(){
    s.clear();
    f.clear();
    g.clear();
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t;
    cin>>t;
    while(t--)solve(),init();
    return 0;
}

D. Turtle Tenacity: Continual Mods

给你一个序列A,选出任意一个排列,要求这个序列连续取模过去最终不等于0。

因为是任意的,显然不需要真的去找出这个序列,只需要判断是否可能。

小的数对大的数取模,还是小的数,不会得到0,所以考虑把序列升序。

如:1 2 3 4 5,最终输出的是最小的那个数。

那么对答案有影响的就是重复的数和最小的数了。

如果最小的有两个,1 1 2 3 4 5,那么不管怎么放最终都会得到0。

大的数对小的数取模,假设y>x,那么y % x得到的值范围在0~x-1之间

所以对最小的数重复的情况进行假设:

x x a b c d e f g

x x后面的取值只有两种可能:是x的倍数,不是x的倍数。

如果是倍数,那么取余不会变得更小,会得到0.

如果不是倍数,那么一定会得到更小的数。

再考虑特殊情况,如果后面都是倍数,倍数和倍数之间能否得到比x更小的数。

x x 2*x 3*x 4*x 6*x

显然是不行的,最少取模出来都是x。

所以不需要后面的数相互去取模判断,只需要和x取模即可。

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define fr first
#define se second
#define endl '\n'
using namespace std;
const int N=1e5+5;

int n,tmp,minn;
map<int,int>f;
vector<int>v;

void solve(){
    cin>>n;
    per(i,1,n){
        cin>>tmp;
        f[tmp]++;
    }

    for(auto [x,y]:f){//第一个就是最小的数
        if(y==1)cout<<"YES"<<endl;//如果只有一个,则YES
        else{
            minn=x;//否则,记录一下最小值
            break;
        }
        return;
    }

    for(auto [x,y]:f){//把所有数都拿出来
        v.push_back(x);
    }

    rep(i,v.size()-1,1){//后面的数和最小的数取模
            int tmp=v[i]%v[0];
            if(tmp!=0 and tmp<minn){//能取出更小的,直接输出YES就行
                cout<<"YES"<<endl;
                return;
            }
    }

    cout<<"NO"<<endl;
}

void init(){
    f.clear();
    v.clear();
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t;
    cin>>t;
    while(t--)solve(),init();
    return 0;
}

补题:E. Turtle vs. Rabbit Race: Optimal Trainings

这道题太可惜了,赛后一会会就做出来了。

lsaac喜欢跑步。

他跑过第一个阶段会得到u的表现分。

他跑过第二个阶段会得到u-1的表现分。

他跑过第三个阶段会得到u-2的表现分。

......

他跑过第k个阶段会得到u+1-k的表现分。

现在有数组a[n],数组中包含阶段数量,如跑过a[1],若a[1]=3,则直接获得3个阶段。

给你数组起始点L,和表现分开头u,问你总表现分最大的情况下,数组结束点R最小是多少。

首先计算一下总表现分,发现是一个公差为1的等差数列,则总表现分为【(首项+尾项)*项数】/2,即((u+(u+1-k))*k)/2

因为我们只需要找R的位置,而且总表现分从开头到结尾是一个开口向下的抛物线(因为u先是正的然后变成负了,求和先变大再变小),再结合题目告诉我们这道题是多次询问,显然要用二分来确定R的位置。

得到总表现分之前需要得到阶段数,显然需要用前缀和去维护一下,一个一个加太慢了。

最后二分的时候,我们可以假设起始点L就是R的位置,拿mid和他比较。

而mid有两个地方有可能比L大,只有左边的mid,才需要L往右走,所以需要再来一个mid-1来和mid的表现分作比较。(有点类似三分了)

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define fr first
#define se second
#define endl '\n'
using namespace std;
const int N=1e5+5;

int n,a[N],q,l,u,f[N];

int p(int k){
    return ((u+(u+1-k))*k)/2;
}

void query(){
    cin>>l>>u;
    //需要最大的表现分
    //f[r]-f[l-1]->x 为当前经过的小节数量
    //第k节获得的表现分y=u+1-k //公差为1的等差数列
    //表现分[u+(u+1-k)]*k/2

//    int ans=l;
//    per(r,l,n){
//        if(p(f[r]-f[l-1])>p(f[ans]-f[l-1])){
//            ans=r;
//        }
//    }
//    cout<<ans<<" ";

     int left=l,right=n;
     while(left<right){
         int mid=(left+right)>>1;
         int rValue=p(f[mid]-f[l-1]);
         int rpValue=p(f[mid-1]-f[l-1]);
         int lValue=p(f[left]-f[l-1]);
         if(rValue>lValue and rpValue<rValue){
             left=mid;
         }else right=mid;
     }
     if(left==n-1)left=p(f[left+1]-f[l-1])>p(f[left]-f[l-1])?left+1:left;
     cout<<left<<" ";
}

void solve(){
    cin>>n;
    per(i,1,n)cin>>a[i],f[i]=f[i-1]+a[i];
    cin>>q;
    per(i,1,q)query();
    cout<<endl;
}

void init(){
    per(i,1,n)f[i]=0;
}
signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t;
    cin>>t;
    while(t--)solve(),init();
    return 0;
}

上面的二分是我手写的,l最终没有和最后一个数比较,如果l==n-1的话,还需要和n去比较一次。

  • 19
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值