ZAFU_2021_1_24_2021寒假个人赛第一场题解

题目的解法不一定唯一,这里给出我自己个人过题时使用的解法。

A题
原题链接:https://codeforces.com/problemset/problem/1370/A
相关tag:规律总结

我们要选择两个不同的数,使得他们的最大公约数尽可能大。
假设我们已经找到了第一个数为x,那么第二个数选择x的整数倍的话,他们的最大公约数都是x。
但是题目已经说了必须选两个不同的数,因此我们选择x和2x,此时最大公约数为x。

2x要满足小于等于n,因此x的最大值就是n/2

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int main()
{
    
    
    IOS
    int t;cin>>t;
    while(t--)
    {
    
    
        int n;cin>>n;
        cout<<n/2<<endl;
    }
}

B题
原题链接:https://codeforces.com/problemset/problem/1027/A
相关tag:规律总结

每个字符都必须要变换一次。
那位置对称的两个字符必须只能是原本就相同,或者ASCII码相差2,一个+1一个-1变成相同的。
for一遍判断是否都满足即可。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int main()
{
    
    
    IOS
    int t;cin>>t;
    while(t--)
    {
    
    
        int n;cin>>n;
        string s;cin>>s;
        bool f=1;
        for(int i=0;i*2<n;i++)
            if(s[i]!=s[n-i-1]&&abs(s[i]-s[n-i-1])!=2) {
    
    f=0;break;}
        if(f) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

C题
原题链接:https://codeforces.com/problemset/problem/1027/B
相关tag:规律总结

我们把横纵坐标加起来等于偶数的点归类为第一类点,横纵坐标加起来是奇数的归类为第二类点。
我们可以先算出第一类点总共有base个
然后根据行标和列标去找当前这个点是从上到下从左到右第几个该类点。如果是第二类点,再加上base个。

接下来是如何计算当前位置是从上到下从左到右第几个点。
注意到第一行里面有a=(n+1)/2个第一类点,那么第一行里第二类点的个数就是b=n-a个点。
而第二行会变为b个第一类点,a个第二类点
第三行会变为a个第一类点,b个第二类点

我们要算第x行第y列是从上到下从左到右第几个点。
第x行这一行,第y列是这一行的第(y+1)/2个点。
前面的x-1行,其中的(x-1)/2对奇偶行可以组成一组,每组有a+b个点。
当(x-1)是奇数的时候,额外加上第x-1行有几个点,视情况加上a或者b。

上述三个加起来便是答案(第二类点多加个base)。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int main()
{
    
    
    IOS
    ll n,q;
    cin>>n>>q;
    ll base=n*n/2+(n%2?1:0);//第一类点共有多少个
    ll a=(n+1)/2,b=n-a;//a代表第一行的第一类点有多少个,b代表第一行的第二类点有多少个
    while(q--)
    {
    
    
        ll x,y;cin>>x>>y;
        ll temp=x+y,out=(y+1)/2+(x-1)/2*(a+b);
        //out为输出的答案,前面的(x-1)/2是两行两行的对数,每对有a+b个点,而(y+1)/2是第x行的点的个数
        if(temp%2)//如果是第二类点
        {
    
    
            out+=base;//值需要加上第一类点的个数
            if(x%2==0) out+=b;//如果x是偶数,前面的x-1行还剩下一行的第二类点未计算,加上对应个数b
        }
        else if(x%2==0) out+=a;//第一类点;如果x是偶数,前面的x-1行还剩下第x-1行的第一类点未计算,加上对应个数a
        cout<<out<<endl;
    }
}

D题
原题链接:https://codeforces.com/problemset/problem/1285/B
相关tag:规律总结

假设存在一个区间[l,r],这些值加起来不小于总共的n个数加起来。
那么对于左侧的区间[1,l-1]和[r+1,n]这两个区间的和x和y,满足x+y<=0。
实际上当x和y出现一个小于等于0的时候,另一侧取到尽头,那么另一个累加和就是0了,必然存在满足条件的区间。

所以直接从左到右和从右到左for两遍算总和,如果出现了小于等于0的值,那就是NO。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int num[100000+7];

int main()
{
    
    
    IOS
    int t;cin>>t;
    while(t--)
    {
    
    
        int n;cin>>n;
        for(int i=0;i<n;i++) cin>>num[i];
        bool flag=1;
        ll sum=0;
        for(int i=0;i+1<n;i++)
        {
    
    
            sum+=num[i];
            if(sum<=0) flag=0;
        }
        sum=0;
        for(int i=n-1;i>0;i--)
        {
    
    
            sum+=num[i];
            if(sum<=0) flag=0;
        }
        if(flag) cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

E题
原题链接:https://codeforces.com/problemset/problem/1027/C
相关tag:数学,结论

我们要构造一个矩形,那么必定是选择两组长度一样的木棒。
另这两根木棒的长度各为a和b。
周长T=a+b,而面积S=a × \times × b
注意到T × \times ×T/S就等于2+(a/b+b/a)
我们要使得a/b+b/a最小,注意到这是个对勾函数。当a和b比值最接近的时候取到最小。

因此我们先对所有木棒排个序,再找到所有长度相同的有哪些,比较长度最接近的即可。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

int main()
{
    
    
    IOS
    int t;cin>>t;
    while(t--)
    {
    
    
        int n;cin>>n;
        vector<int>num(n);
        for(int i=0;i<n;i++) cin>>num[i];
        sort(num.begin(),num.end());
        vector<int>data;//有哪些长度相等的一对木棒
        for(int i=0;i+1<n;i++)
        {
    
    
            if(num[i]==num[i+1])//第i根和第i+1根凑成一对
            {
    
    
                data.push_back(num[i]);
                i++;
            }
        }
        int tar=0;
        for(int i=1;i+1<data.size();i++)
        {
    
    
            if(data[tar+1]*data[i]>data[tar]*data[i+1]) tar=i;//转换成乘法,避免了乘法运算,找到data[i]/data[i+1]最小的位置
        }
        cout<<data[tar]<<' '<<data[tar]<<' '<<data[tar+1]<<' '<<data[tar+1]<<endl;
    }
}

F题
原题链接:https://codeforces.com/problemset/problem/817/C
相关tag:二分

对于整数x,我们把它十进制各位加起来的值记为temp。
注意到x的值每+1,由于十进制存在进位,temp的值可能+1,也可能减少。
也就是说x和temp的差值,随着x的增大,只可能越变越大,不会越来越小。

满足二分的条件,二分去找不满足的最大的值为多少,用n减去它就是满足的个数了。

#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;

ll change(ll x)
{
    
    
    ll ret=0;
    while(x)
    {
    
    
        ret+=x%10;
        x/=10;
    }
    return ret;
}

int main()
{
    
    
    IOS
    ll n,s;cin>>n>>s;
    ll l=0,r=n;
    while(l<r)
    {
    
    
        ll mid=(l+r)/2+1;
        if(mid-change(mid)>=s) r=mid-1;
        else l=mid;
    }
    cout<<n-l<<endl;
}

猜你喜欢

转载自blog.csdn.net/StandNotAlone/article/details/113091528