iwtgm-3

题目链接

A.

一个数组可以头尾两端取,求取到最小值和最大值所需最小步数
从两头分别取或者从左向右取完或从右向左取完,三种情况取最小的步数即可

void solve()
{
    int n;cin>>n;
    int mi=150,id_mi,ma=-1,id_ma;
    for(int i=1,x;i<=n;i++){
        cin>>x;
        if(x>ma){
            ma=x;id_ma=i;
        }
        if(x<mi){
            mi=x;id_mi=i;
        }
    }
    if(id_ma<id_mi)swap(id_mi,id_ma);
    cout<<min({id_mi+n-id_ma+1,id_ma,n-id_mi+1})<<endl;
}

B.

先算平均数,然后超过平均数的一定是要分出去的,超过平均数的个数即为答案

ll a[N];
void solve()
{
    int n;cin>>n;
    ll sum=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    if(sum%n!=0){
        cout<<-1<<endl;return ;
    }
    ll cnt=0;
    sum=sum/n;
    for(int i=1;i<=n;i++){
        if(a[i]>sum)cnt++;
    }
    cout<<cnt<<endl;
}

C.

求序列和在l-r范围内的子序列的个数
把数组排序,然后对于每一个数去找满足条件的最小值和最大值,两者之差+1即为该数的贡献
推荐用lower_bound()和upper_bound()代码少
自己手写的二分错了,后来队友帮忙把二分刚好满足条件退出的break去掉也过了,特别鸣谢@north_

简洁版:

ll a[N];
void solve()
{
    memset(a,0,sizeof(a));
    ll n,l,r;cin>>n>>l>>r;
    for(int i=0;i<n;i++)cin>>a[i];
    sort(a,a+n);
    ll ans=0;
    for(int i=0;i<n;i++){
        ll l0= lower_bound(a+1+i,a+n,l-a[i])-a;
        ll r0= upper_bound(a+1+i,a+n,r-a[i])-a;
        ans+=r0-l0;
    }
    cout<<ans<<endl;
}

手写二分版:

void solve()
{
    memset(a,0,sizeof(a));
    ll n,l,r;cin>>n>>l>>r;
    for(int i=1;i<=n;i++)cin>>a[i];
    sort(a+1,a+1+n);
    ll ans=0;
    for(int i=1;i<n;i++){
        ll x=a[i];
        ll left=i+1,right=n,mid;
        ll idx_left=0,idx_right=0;
        while(left<=right){
            mid=(left+right)/2;
            if(x+a[mid]<l){
                left=mid+1;
            }
            else {
                idx_left=mid;right=mid-1;
            }
        }
        left=idx_left;right=n;
        if(idx_left==0||x+a[idx_left]>r)continue;
        while(left<=right){
            mid=(left+right)/2;
            if(x+a[mid]>r){
                right=mid-1;
            }else {
                idx_right=mid;left=mid+1;
            }
        }
        if(idx_right==0||x+a[idx_right]<l)continue;
        //cout<<"idx_left="<<idx_left<<' '<<"idx_right="<<idx_right<<endl;
        ans+=idx_right-idx_left+1;
    }
    cout<<ans<<endl;
}

D.

给两个数,操作是能把该数替换成任何一个它的因数,问能否刚好k次操作让两个数相等

k=0,两个数必须相等
k=1,两个数必须存在倍数关系
其他情况我们把两个数的质因数个数算出来,>k,则满足条件<k,则不满足条件

之前自己的想法是分类讨论了质数和质数,合数和合数,质数和合数
因为数据范围是1e9,筛不了这么大的质数就放弃了
但是按循环跑如果出一个1e9左右范围的质数那也要跑1e9啊,数据水了?

int count(int x,int k){
    for(int i=2;i*i<=x;i++){
        while(x%i==0){
            x/=i;
            k--;
        }
    }
    if(x>1)k--;
    return k;
}
void solve()
{
    int a,b,k;cin>>a>>b>>k;
    if(k==1){
        if((a%b==0||b%a==0)&&a!=b){
            cout<<"YES"<<endl;return ;
        }else {
            cout<<"NO"<<endl;return ;
        }
    }
    k=count(a,k);
    k=count(b,k);
    cout<<(k>0?"NO":"YES")<<endl;
}

G.

让n<m,a<b,首先如果一个礼盒都凑不齐就输出0
特判a==b的情况,答案为n/a
基本原则是让大的取大的,但直接模拟会超时,于是考虑:
先取一些(a,b)这样的二元组,再取一些(a+b,a+b)这样的二元组最优,
证明:
选一个二元组(a,b)会让(n,m)之间的差缩减(b-a),
当第一次让n>m时,之后m和n的差总小等于a和b的差,也就是大小关系会反复换过来
所以我们可以先选(m − n)/(b-a)个(a,b),向下取整和多取一个两种情况
同时要考虑到n最多有多少个a,m最多有多少个b,三者取一个最小值
剩下的可以全选(a+b,a+b)
取两种情况两个值的较大值

void solve()
{
    int x,y,a,b;cin>>x>>y>>a>>b;
    if(x>y)swap(x,y);
    if(a>b)swap(a,b);
    if(x<a||y<b){
        cout<<0<<endl;return ;
    }
    int res=y-x,lim=b-a;
    if(!lim){
        cout<<min(x,y)/a<<endl;return ;
    }
    int cur=min(res/lim,min(x/a,y/b));
    x-=cur*a;y-=cur*b;
    int ans=cur+min(x,y)/(a+b)*2;
    if(x>=a&&y>=b){
        x-=a;y-=b;cur++;//位置1
    }
    cout<<max(ans,cur+min(x,y)/(a+b)*2)<<endl;
}

F.

l和r,分别算有多少个1,多少个10,多少个100...
然后再相减

ll base[15]={1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000};
void solve()
{
    ll l,r;cin>>l>>r;
    ll ans=0,res=0;
    for(int i=0;i<10;i++){
        ans+=r/base[i];
        res+=l/base[i];
    }
    cout<<(ans-res)<<endl;
}

E.

输入掉大分,字符串里有空格,直接分成多个字符串
计算单个字符串的贡献,
有链接操作数据量太大,可以只保留开头和结尾,中间已经算过贡献的舍去

string s;
map<string,string>ma;
map<string,ll>ans;
string getfirst(string x){
    if(x.size()<3)return x;
    else return x.substr(0,3);
}
string getlast(string x){
    if(x.size()<3)return x;
    else return x.substr(x.size()-3,3);
}
ll count(string a,string b){
    ll cnt=0;
    for(int i=0;i+b.size()<=a.size();i++){
        if(a.substr(i,b.size())==b)++cnt;
    }
    return cnt;
}
void solve()
{
    ma.clear();ans.clear();
    ll n;cin>>n;
    ll tmp=0;
    for(int i=1;i<=n;i++){
        string a,b,c,d,e;cin>>a>>b>>c;
        if(b==":="){
            ans[a]= count(c,"haha");
            ma[a]=c;
        }else{
            cin>>d>>e;
            string f=ma[c]+ma[e];
            ans[a]=ans[c]+ans[e]+count(getlast(ma[c])+getfirst(ma[e]),"haha");
            if(f.size()>=7)f= getfirst(f)+"@"+getlast(f);
            ma[a]=f;
        }
        tmp=ans[a];
    }
    cout<<tmp<<endl;
}
posted @ 2023-11-02 13:22  WW爆米花  阅读(4)  评论(0编辑  收藏  举报