Codeforces Round #725 (Div. 3)

Codeforces Round #725 (Div. 3)

A. Stone Game

在这里插入图片描述

Example

input

5
5
1 5 4 3 2
8
2 1 3 4 5 6 8 7
8
4 2 3 1 8 6 7 5
4
3 4 2 1
4
2 3 1 4

output

2
4
5
3
2

题目大意:

给你一个数组(1-n每个数出现一次),你每次只能从最左或最右边删掉一个元素,求最少步数删除最大值和最小值。

思路:

贪心,先找出最大值和最小值的位置,答案就是:只从左边删,只从右边删,从左右两边删 里的最少步数。

代码:

void solve()
{
    int n;
    cin>>n;
    int pos1,pos2;
    for(int i=1;i<=n;++i)
    {
        int a;
        cin>>a;
        if(a==1)pos1=i;
        if(a==n)pos2=i;
    }
    cout<<min(min(max(pos1,pos2),max(n-pos1+1,n-pos2+1)),min(pos1+n-pos2+1,pos2+n-pos1+1))<<endl;
}
 
signed main()
{
    fastio();
    int t;
    t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

B. Friends and Candies

在这里插入图片描述

Example

input

5
4
4 5 2 5
2
0 4
5
10 8 5 1 4
1
10000
7
1 1 1 1 1 1 1

output

2
1
-1
0
0

题目大意:

给你一个数组,你可以选择k个元素,任意分配这k个元素的值给他们或者数组里的其他元素,最终使得所有元素的值都相同,求最小的k。

思路:

数学+贪心。先求出sum,不能整除当然就不可能存在答案,如果能整除,算出平均值,我们可以总是选择大于平均值的数,让他们多出来的部分配到其他小的数上,选择的元素个数就是大于平均值的数的个数。

代码:

const int N=2e5+7;
int a[N];

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

signed main()
{
    fastio();
    int t;
    t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

C. Number of Pairs

在这里插入图片描述

Example

input

4
3 4 7
5 1 2
5 5 8
5 1 2 4 3
4 100 1000
1 1 1 1
5 9 13
2 5 5 1 1

output

2
7
0
1

题目大意:

给你一个数组,求任一对数之和在l到r范围之间有多少组。

思路:

暴力O(n*n)必 W a,所以我要相信存在某种复杂度更低的方法。

二分。因为l<=a[i]+a[j]<=r,所以l-a[j]<=a[i]<=r-a[j],所以我们可以先O(n)遍历每个元素,然后对每个元素二分(记得排序啊),复杂度是O(n*log n),注意,题目要求元素下标必须i<j,所以二分的时候范围注意一下。

代码:

const int N=2e5+7;
int a[N];
 
void solve()
{
    int n;
    int l,r;
    cin>>n;
    cin>>l>>r;
    for(int i=0;i<n;++i)
    {
        cin>>a[i];
    }
    sort(a,a+n);
    int ans=0;
    for(int i=0;i<n;++i)
    {
        int templ=l-a[i];
        int tempr=r-a[i];
        ans+=upper_bound(a+i+1,a+n,tempr)-lower_bound(a+i+1,a+n,templ);
    }
    cout<<ans<<endl;
}
 
signed main()
{
    fastio();
    int t;
    t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

D. Another Problem About Dividing Numbers

在这里插入图片描述

Example

input

8
36 48 2
36 48 3
36 48 4
2 8 1
2 8 2
1000000000 1000000000 1000000000
1 2 1
2 2 1

output

YES
YES
YES
YES
YES
NO
YES
NO

题目大意:

给三个数 a,b,k,你每次可以选择a或b除以c,其中c是你选择的数的一个约数(不能是1),问是否能恰好k次之后两数相等。

思路:

数学,数论。模拟一下几个样例就发现了,最后能否相同关键看a和b的约数个数。比如a,因为c是你随便选的,所以你甚至可以直接除以a,之后等于1,b同理,所以你至少可以2次就相等。下限有了,那么上限呢。我们分别把a,b质因数分解,因数之和就是你能除的最大次数。然后k等于1的时候特判一下,k = = 1时,若a = = b,你必须除一个数,所以最后不可能相等,若a整除b或b整除a,你总是可以除其中的一个余数使其相等。

这里用的是sqrt(n)的方法求数的约数个数,复杂度是O(n*sqrt(n)),也可以用倍数法求整个区间的数的约数个数,然后O(1)查询,会比较快。

代码:

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;
        else cout<<"NO"<<endl;
    }
    else
    {
        int numa=0;
        int numb=0;
        for(int i=2;i*i<=a;++i)
        {
            if(a%i==0)
            {
                a/=i;
                ++numa;
                while(a%i==0)
                {
                    a/=i;
                    ++numa;
                }
            }
        }
        if(a>1)++numa;
        for(int i=2;i*i<=b;++i)
        {
            if(b%i==0)
            {
                b/=i;
                ++numb;
                while(b%i==0)
                {
                    b/=i;
                    ++numb;
                }
            }
        }
        if(b>1)++numb;
        if(k>=0&&k<=numb+numa)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

signed main()
{
    fastio();
    int t;
    t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

E. Funny Substrings

在这里插入图片描述

Example

input

4
6
a := h
b := aha
c = a + b
c = c + c
e = c + c
d = a + c
15
x := haha
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
x = x + x
1
haha := hah
5
haahh := aaaha
ahhhh = haahh + haahh
haahh = haahh + haahh
ahhhh = ahhhh + haahh
ahhaa = haahh + ahhhh

output

3
32767
0
0

题目大意:

模拟一种语言,只有 x := s 和 x = a + b 这两种语句,求最后一句得出的字符串有多少个haha(可重叠计算)

思路:

根据这个语言的特点和n的取值范围,最后字符串的长度最大将近2^50,已经超出c++的存储范围了(你要是会python,直接模拟就过了…),因此我们可以先学会py再来做这题 因此我们要用到一些处理技巧。

注意到我们的模式串是"haha",长度为4,我们算出的结果没必要保存全部的字符,我们只需保存前三个字符和后三个字符就行了,当合并的时候,我们让前一个字符串的后缀与后一个字符串的前缀合并,求一次模式串的数量,再加上两个字符串之前就已经算出来的自己的模式串的数量,就是合并之后的数量。

较大的模拟题中,将各个部分的功能写成函数是一种好习惯。

代码:

map<string,int>ans;

int count(const string& s, const string& p)
{
    int cnt = 0;
    for (int i = 0; i + p.size() <= s.size(); i++)
    {
        if (s.substr(i, p.size()) == p)
        {
            cnt++;
        }
    }
    return cnt;
}
string getFirst(string s)
{
    if (s.size() < 3)
    {
        return s;
    }
    return s.substr(0, 3);
}
string getLast(string s)
{
    if (s.size() < 3)
    {
        return s;
    }
    return s.substr(s.size() - 3, 3);
}

void solve()
{
    map<string,string>ma;//记录前缀和后缀
    ans.clear();
    int n;
    cin>>n;
    int temp2=0;
    for(int i=0;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;
        }
        temp2=ans[a];
    }
    cout<<temp2<<endl;
}

signed main()
{
    fastio();
    int t;
    t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

F. Interesting Function

在这里插入图片描述

Example

input

4
1 9
9 10
10 20
1 1000000000

output

8
2
11
1111111110

题目大意:

给两个数l和r(l<r),l每次+1,问加到r 数位总共变了多少次。

思路:

看最后一个样例不难有个猜想,通过模拟题意也可以发现,从0到10需要花11次,从0到100需要花111次,所以我们可以用0到r的次数减去0到l的次数,就是l到r的次数(这其实是一种数位dp的思想)。

假设f(x)是一个函数,它可以返回x这个数的每一位都是1的数,且r=123,那么次数就等于f(1)* 3+f(10)* 2+f(100)* 1.

代码:

int qpow(int a,int b){int ans=1;while(b){if(b&1)ans=ans*a;b>>=1;a=a*a;}return ans;}
int weishu(int n)
{
    int ans=0;
    while(n)
    {
        n/=10;
        ++ans;
    }
    return ans;
}

int cal(int n)
{
    if(weishu(n)==1)return n;
    int ans=0;
    while(n)
    {
        n/=10;
        ++ans;
    }
    int num=0;
    while(ans--)
    {
        num=num*10+1;
    }
    return num;
}

void solve()
{
    int l,r;
    cin>>l>>r;
    int numl=0;
    int numr=0;
    int xishu=0;
    while(r)
    {
        int tempr=r%10;
        r/=10;
        int a=cal(qpow(10,xishu++))*tempr;
        numr+=a;
    }
    xishu=0;
    while(l)
    {
        int templ=l%10;
        l/=10;
        int a=cal(qpow(10,xishu++))*templ;
        numl+=a;
    }
    cout<<numr-numl<<endl;
}

signed main()
{
    fastio();
    int t;
    t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}

G. Gift Set

在这里插入图片描述

Example

input

9
10 12 2 5
1 1 2 2
52 311 13 27
1000000000 1000000000 1 1
1000000000 1 1 1000000000
1 1000000000 1000000000 1
1 2 1 1
7 8 1 2
4 1 2 3

output

3
0
4
1000000000
1
1
1
5
0

题目大意:
有四个数字x,y,a,b,其中x,y分别是红色糖果和蓝色糖果的数量,你每次只能把a个红色糖果b个蓝色糖果或b个红色糖果a个蓝色糖果放进一个包裹里,问最后最多能放多少个包裹。

思路:

这题有很多做法,官方给的二分,好像还有一些大佬做成几何题,我这里用数学+贪心的方法。

我们先保证x<=y,a<=b(如果大于就交换).

当a==b时,答案就是min(x/a,y/a).

当a!=b时:

​ 我们注意到 我们可以贪心让x全部用a装,y全部用b装,但是有另一种情况是在这样贪心之后,x和y的相对大小会变。这是因为y-x<b-a,减的时候右边变化的速率大于左边变化的速率,所以不能直接这样贪心,但是我们可以在相对大小改变之前,减去x和y相差的那部分,让x和y相近,然后x用a,y用b,x用b,y用a,一顿偶数次操作之后x和y的相对差值就不会变,最后再检查一下是否可以再包一个包裹就行了。

代码:

void solve()
{
    int x,y,a,b;
    cin>>x>>y>>a>>b;
    if(a==b)
    {
        cout<<min(x,y)/a<<endl;
        return;
    }
    if(x>y)swap(x,y);
    if(a>b)swap(a,b);
    int ans=0;
    if(y-x>b-a)//贪心之后相对大小不会变
    {
        int temp=min(min((y-x)/(b-a),x/a),y/b);
        x-=a*temp;
        y-=b*temp;
        ans+=temp;
    }
    int Min=min(x/(a+b),y/(a+b));//成对
    ans+=Min*2;
    x-=Min*(a+b);
    y-=Min*(a+b);
    if(x>=a&&y>=b||x>=b&&y>=a)ans++;
    cout<<ans<<endl;
}
 
signed main()
{
    fastio();
    int t;
    t=1;
    cin>>t;
    while(t--)
    {
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值