ZAFU_2021_1_26_2021寒假个人赛第二场题解

A题
原题链接:https://codeforces.com/problemset/problem/884/A
相关tag:施行

每天有86400秒,写了x秒的作业,还有86400-x秒可以练歌。(还是很想吐槽这个不需要睡觉的设定)
直接一天天的依次算过去,哪一天练得时间足够了就输出。

#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 n,t;cin>>n>>t;
    for(int i=1;i<=n;i++)
    {
    
    
        int x;cin>>x;
        t-=86400-x;
        if(t<=0) {
    
    cout<<i<<endl;break;}
    }
}

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

很容易发现偶数只会经过1次变换,值-1。
而奇数会先+1,再在下一次操作中-1还原。

因此把输入的奇数原样输出,偶数-1输出即可。

#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 n;cin>>n;
    while(n--)
    {
    
    
        int a;cin>>a;
        if(a&1) cout<<a<<' ';
        else cout<<a-1<<' ';//CF对格式的要求不是非常严格,只有单行输出的时候,末尾空格和换行不做严格要求
        //除了cf的比赛外尤其正式比赛不要这么写,怎么规矩怎么来
    }
}

C题
原题链接:https://codeforces.com/problemset/problem/1216/A
相关tag:字符串

所有前缀偶数长度的区间里a和b出现的次数要相同。
容易用归纳法得到,如果下标i(下标从0开始)是奇数,那么s[i]和s[i-1]必定一个是a一个是b。

所以直接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 n;cin>>n;
    string s;cin>>s;
    int ans=0;
    for(int i=0;i<n;i++)
    {
    
    
        if(i%2&&s[i]==s[i-1])
        {
    
    
            if(s[i]=='a') s[i-1]='b';
            else s[i-1]='a';
            ans++;
        }
    }
    cout<<ans<<endl;
    cout<<s<<endl;
}

D题
原题:LightOJ - 1138
相关tag:数学,二分,无穷级数,进制分解
(提供三种解法的思路和代码)

末尾有几个零,其实也就是最后的结果最多有几个10相乘,也就是分解质因子后最多有多少对2和5。
2的个数必定多于5的个数,因此我们只需要计算5的个数有多少个就是当前n!末尾有多少个0。

注意到随着n的增大,末尾0的个数也一直在增多,满足二分条件。

至于计算5的个数,
能整除5但不能整除25的,有1个5,需要被计算1次
能整除5和25但不能整除125的,有2个5,需要被计算2次
能整除5和25和125但不能整除625的,有3个5,需要被计算3次

n/5可以算出能整除5的有哪些,n/25可以算出整除25的有哪些…依次累加即可。

#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 cal(int x)
{
    
    
    int ret=0;
    while(x)//计算x!分解质因数后有多少个5
    {
    
    
        ret+=x/5;
        x/=5;
    }
    return ret;
}

int main()
{
    
    
    IOS;//使用之后不要混用cin和scanf printf
    int t;cin>>t;
    for(int CASE=1;CASE<=t;CASE++)
    {
    
    
        int m;cin>>m;
        int l=5,r=5e8;//右边界开一个足够大的就行了,直接5乘以最大的m的范围必够,不需要再多过脑子
        while(l<r)
        {
    
    
            int mid=(l+r)>>1;
            if(cal(mid)<m) l=mid+1;
            else r=mid;
        }
        cout<<"Case "<<CASE<<": ";
        if(cal(l)==m) cout<<l<<endl;
        else cout<<"impossible"<<endl;
    }
}

再就是使用无穷级数的优化方法,这种方法需要数学基础和数学敏感性。
我们需要枚举的实际上只有5 × \times ×x也就是5的倍数的值。我们可以枚举x,注意到最后要m个零。
可以利用无穷级数计算出0.8m × \times ×(0.2)0+0.8m × \times ×(0.2)1+0.8m × \times ×(0.2)2+0.8m × \times ×(0.2)3………<m,所以可以由此直接从0.8m开始暴力枚举去找对应的值。
由于无穷级数已经非常接近目标的m值,枚举时的次数会非常少。
在这里插入图片描述

#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 cal(int x)
{
    
    
    int ret=0;
    while(x)//计算x!分解质因数后有多少个5
    {
    
    
        ret+=x/5;
        x/=5;
    }
    return ret;
}

int main()
{
    
    
    IOS;//使用之后不要混用cin和scanf printf
    int t;cin>>t;
    for(int CASE=1;CASE<=t;CASE++)
    {
    
    
        int m;cin>>m;
        cout<<"Case "<<CASE<<": ";
        int base=m*4/5;
        for(int i=base;i<=base+10;i++)
        {
    
    
            if(cal(i*5)>=m)
            {
    
    
                if(cal(i*5)==m) cout<<i*5<<endl;
                else cout<<"impossible"<<endl;
                break;
            }
        }
    }
}

再就是我们可以用类似进制分解的方法

实际上我们可以发现
n每增加1次5的倍数,但是不超过25的时候,0的个数加1
n每增加1次25的倍数,但是不超过125的时候,0的个数加1 × \times × 5+1=6
n每增加1次125的倍数,但是不超过625的时候,0的个数加6 × \times × 5+1=31

我们可以按照5的不同指数长度,也就是按照5进制把目标的n分割。
如果有某一项需要的系数超过4就代表不存在满足的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;

int t, n;
ll a[20],b[20];
//b[i]保存5^i的值是多少
//a[i]数组存储b[i]!分解质因数后有多少个5,实际上也是n的值每增加长度b[i]会增加的质因子5的个数

//特别注意此处每个区间b[i]的系数不可以超过4达到5以上,借此判定是否impossible

int main()
{
    
    
    IOS
    a[1]=1;
    b[1]=5;
    for (int i = 2; i <= 14; i ++ )
    {
    
    
        a[i]=a[i-1]*5+1;
        b[i]=b[i-1]*5;
    }
    cin>>t;
    for(int CASE=1;CASE<=t;CASE++)
    {
    
    
        cin>>n;
        ll ans=0;
        bool f=1;
        for (int i=13;i>=1;i--)
        {
    
    
            int sum = 0;
            while(n>=a[i])
            {
    
    
                n-=a[i];
                sum++;
            }
            if(sum>4) {
    
    f=0;break;}
            else ans+=sum*b[i];
        }
        cout<<"Case "<<CASE<<": ";
        if(f) cout<<ans<<endl;
        else cout<<"impossible"<<endl;
    }
}

E题
原题链接:https://codeforces.com/problemset/problem/510/B
相关tag:dfs

找图里有没有圈,可以使用dfs搜索来实现,用一个cas记录当前是第几次搜索,在第cas次搜索中搜索到的点都用cas标记。
单次搜索如果搜索回到了本次搜索之前搜索过了的位置,代表出现了圈。

#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;

string field[57];//存图
int flag[57][57];//每个位置的标记,0代表未被搜索过,非0值代表其在第几次搜索被搜索过
bool f=0;//记录是否存在圈
int n,m,cas=0;//cas记录当前是第几次搜索

int dir[4][2]=//存储上下左右四个方向
{
    
    
    1,0,
    -1,0,
    0,1,
    0,-1
};

void dfs(int x,int y,int prex,int prey)//x,y为当前的行标列标,prex和prey为上一步的行标和列标
{
    
    
    flag[x][y]=cas;
    for(int i=0;i<4;i++)//朝四个方向走
    {
    
    
        int nextx=x+dir[i][0];//下一步的位置
        int nexty=y+dir[i][1];
        if((nextx!=prex||nexty!=prey)&&nextx>=0&&nextx<n&&nexty>=0&&nexty<m)
            //不能走回头路,且不可以走出地图外
        {
    
    
            if(field[nextx][nexty]==field[x][y])//字符要相同
            {
    
    
                if(!flag[nextx][nexty]) dfs(nextx,nexty,x,y);//如果是未被搜索过的点,继续执行搜索
                else if(flag[nextx][nexty]==flag[x][y]) f=1;//如果是已经被搜索过的点,且是当前这次搜索搜到的点,代表出现了圈
            }
        }
    }
}

int main()
{
    
    
    IOS
    cin>>n>>m;
    for(int i=0;i<n;i++) cin>>field[i];
    for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
            if(!flag[i][j])//如果发现未被搜索过的点,执行新的一次搜索
            {
    
    
                cas++;
                dfs(i,j,-1,-1);
            }
    if(f) cout<<"Yes"<<endl;
    else cout<<"No"<<endl;
}

F题
原题:hdoj6794 (2020暑期多校第二场签到题…噩梦的多校难度)
相关tag:前缀和,dp思想

看代码吧,头太疼受不了了,思路过程就不写了,注释很详细。

#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;
const int maxn=1e5+7;

int dp[maxn];//dp[i]记录前i个数字能组成的p最多有几个
int last[maxn];//last[i]记录前缀和对p取模,最后一次出现在哪个位置

int main()
{
    
    
    IOS
    int t;cin>>t;
    while(t--)
    {
    
    
        memset(dp,0,sizeof(dp));
        memset(last,-1,sizeof(last));//-1代表还未出现过
        last[0]=0;//一个数都不取和为0,在位置0出现
        int sum=0;
        int n,p;cin>>n>>p;
        for(int i=1;i<=n;i++)
        {
    
    
            int x;cin>>x;
            sum=(sum+x)%p;
            int pre=last[(sum+p)%p];//注意这是对p取模的结果,(sum+p)%p与sum的前缀和相差为p的整数倍
            if(pre!=-1) dp[i]=max(dp[i-1],dp[pre]+1);//如果pre出现过,有两种选择方案
            //一种是pre到当前i位置的所有数凑成一个p的整数倍,方案数为dp[pre]+1
            //另一种则是不组合成p的整数倍,继承i-1位置的情况
            else dp[i]=dp[i-1];
            last[sum]=i;//更新前缀和%p的最后一次出现位置
        }
        cout<<dp[n]<<endl;
    }
}

G题
原题链接:https://codeforces.com/problemset/problem/510/B
相关tag:数学

对于[x,[k/[k/x]]]这个区间的所有整数i,[k/i]的值都相等。
证明过程见《算法竞赛进阶指南》P141页最下面的部分

借助该结论实现O(t × \times ×sqrt(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;

int main()
{
    
    
    IOS
    int t;cin>>t;
    while(t--)
    {
    
    
        vector<int>ans;
        int n;cin>>n;
        ans.push_back(0);
        int now=n;
        while(now)
        {
    
    
            ans.push_back(n/now);
            now=n/(n/now+1);
        }
        cout<<ans.size()<<endl;
        for(int i=0;i<ans.size();i++)
        {
    
    
            if(i) cout<<' ';
            cout<<ans[i];
        }
        cout<<endl;
    }
}

H题
原题链接:https://codeforces.com/problemset/problem/1285/D
相关tag:分治,dfs

这道题的思路是分解成二进制后从最高位开始看,如果只出现0或者1的话那么答案的这一位就是0
如果又出现0又出现1点话,答案的这一位就是1,再根据0还是1分成两个部分去看下一位的情况,取这两个部分的最小值

#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;

vector<int>a;

int solve(vector<int>&b,int bit) //&重复利用数组a降低内存消耗,bit为当前正在判断的位数
{
    
    
    if(b.size()==0||bit<0) return 0;
    vector<int>l,r;
    for(int i=0;i<b.size();i++)
    {
    
    
        if(b[i]>>bit&1) l.push_back(b[i]); //和1进行与运算就能判断末尾是1还是0
        else r.push_back(b[i]);
    }
    if(l.size()==0) return solve(r,bit-1);
    if(r.size()==0) return solve(l,bit-1);
    return min(solve(l,bit-1),solve(r,bit-1))+(1<<bit);
}

int main()
{
    
    
    int n;
    scanf("%d",&n);
    a.resize(n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    printf("%d\n",solve(a,30));     //最大数据没超过2^30
}

猜你喜欢

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