Educational Codeforces Round 5 ABCDE

Problem A

位数少的前面补零,比字典序,注意本身可能有前导0

#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define debug(x) printf("----Line%s----\n",#x)

using namespace std;

const int N = 1e6+5;

char s[N],ss[N],s0[N];

int main()
{
    //int T;scanf("%d",&T);while (T--){}
    //int n;while (~scanf("%d",&n)){}
    while (~scanf("%s %s",s,ss)){
        int len = strlen(s);
        int llen = strlen(ss);
        if (len>llen){///第二个串前面补0
            for (int i=0;i0) printf(">\n");
            else if (strcmp(s,s0)==0) printf("=\n");
            else printf("<\n");
        }
        else if (llen>len){
            for (int i=0;i0) printf(">\n");
            else if (strcmp(s0,ss)==0) printf("=\n");
            else printf("<\n");
        }
        else {
            if (strcmp(s,ss)>0) printf(">\n");
            else if (strcmp(s,ss)==0) printf("=\n");
            else printf("<\n");
        }
    }
    return 0;
}

Problem B

找每行的最小值,取每行最小值的最大值即可

#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define debug(x) printf("----Line%s----\n",#x)
#define INF 0x3f3f3f3f
using namespace std;

const int N = 1e5+5;

int a[110][110];

int main()
{
    //int T;scanf("%d",&T);while (T--){}
    //
    int n,m;
    while (~scanf("%d %d",&n,&m)){
        int min1 = 0,max2 = -INF;
        for (int i=0;i

Problem C

bfs白块并标记掉下次不再遍历,每次bfs记录白块的数量并统计所有本次bfs撞到的实心块,本次统计得到的白块数量加到每一个撞到的实心块的答案中

#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define debug(x) printf("----Line%s----\n",#x)
#define for0(i,a,b) for (int i=a;i P;
queue

getans; int ans[N][N],n,m; queue

q; char mp[N][N]; bool again[N][N]; void BFS(int stx,int sty) { int cnt = 0; while (!q.empty()) q.pop(); q.push({stx,sty}); vis[stx][sty] = true; while (!q.empty()){ P now = q.front(); q.pop(); cnt++; for0(i,0,4){ int nowx = now.fi+dx[i]; int nowy = now.se+dy[i]; if (nowx<=0||nowx>n || nowy<=0||nowy>m) continue; if (mp[nowx][nowy]=='*'){ if (again[nowx][nowy]) continue; getans.push({nowx,nowy}); again[nowx][nowy] = true; continue; } if (mp[nowx][nowy]=='.' && !vis[nowx][nowy]) q.push({nowx,nowy}),vis[nowx][nowy]=true; } } while (!getans.empty()){ P now = getans.front();getans.pop(); again[now.fi][now.se] = false; ans[now.fi][now.se] += cnt; } } int main() { while (~scanf("%d %d",&n,&m)){ memset(vis,0,false); memset(ans,0,sizeof ans); for (int i=1;i<=n;i++) scanf("%s",mp[i]+1); for1(i,1,n){ for1(j,1,m){ if (mp[i][j]=='.' && !vis[i][j]) BFS(i,j); } } for1(i,1,n){ for1(j,1,m){ if (mp[i][j]=='.') printf("."); else printf("%d",(ans[i][j]+1)%10); } printf("\n"); } } return 0; }


Problem D

两个指针,一个指向起点,一个指向当前最远距离,第一个指针右移时第二个指针做出对应改变就行

#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define debug(x) printf("----Line%s----\n",#x)

using namespace std;

const int N = 5e5+5;
const int MAXA = 1e6+5;

int cnt[MAXA];///记录数字种类
int n,k,nowcnt;
int v[N];
int ans[N];

int main()
{
    while (~scanf("%d %d",&n,&k)){
        memset(cnt,0,sizeof cnt);
        nowcnt = 0;

        for (int i=1;i<=n;i++) scanf("%d",v+i);

        int nowstop = 0;///当前K个不同的数最远停在那儿
        for (int i=1;i<=n;i++){
            if (i>1){  ///第一个数之前没有要去掉的,防止有0错掉
                cnt[v[i-1]]--;
                if (cnt[v[i-1]]==0) nowcnt--;
            }
            int now = nowstop+1;///下一位要加紧当前最长长度的

            while (now<=n && nowcnt<=k-1){
                if (cnt[v[now]]==0) nowcnt++;
                cnt[v[now]]++;

                now++;
                while (now <=n && nowcnt==k && cnt[v[now]]>0){///使得长度最长
                    cnt[v[now]]++;
                    now++;

                }
            }
            nowstop = max(now-1,nowstop);
            ans[i] = nowstop-i+1;///当前为起点的最长长度
        }

        int maxlen=0,maxpos=0;
        for (int i=1;i<=n;i++){
            if (ans[i]>maxlen){maxlen = ans[i];maxpos = i;}
        }
        printf("%d %d\n",maxpos,maxpos+maxlen-1);
    }

    return 0;
}

Problem E

首先:f(n,m)表示n%1+....+n%m,那么有以下两种情况

n

n>=m,ans = f(n,n-1) + (m-n)*n

所以我们只需求m小于n的情况

对于f(n,m)公式化简:(CF原版题解截的图)

我们的任务就是求出[n/i]*i的前m项之和

我们发现当

n/2+1<=i<=n,时,[n/i] = 1

n/3+1<=i<=n/2时, [n/i]=2

n/x+1<=i<=n/(x-1)时,[n/i]=x-1

这意味着这意味这一段区间[n/i]都是一样的,只有i不同,可以直接整段一次求和。

利用这个性质我们把复杂度控制在O(sqrt(N))

可能有点分块的思想:

对于1~(n/sqrt(n))暴力计算

对于(n/sqrt(n)+1)~m 整段计算

这里有两个注意点:

①为什么分段点是n/sqrt(n)?

因为我把从[n/sqrt(n)+1,m]的区间整段计算,那么第一段左边界是 n/sqrt(n)+1,因为n一般不等于sqrt(n)*sqrt(n),所以这样不会出错也比较方便

②利用公式整段求和有除法取余操作,要用到逆元,否则出错。

逆元参考:https://blog.csdn.net/weixin_43768644/article/details/94768449

代码:(那个恶心的sum宏定义就是区间计算,以及除法变乘,逆元取模)

#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define debug(x) printf("----Line%s----\n",#x)
#define for0(i,a,b) for (ll i=a;i>= 1;
        a = (a*a)%mod;
    }
    return ans;
}

ll sb = fast_power(2,mod-2);//

ll f(ll n,ll m)
{
    ll ans = (n%mod)*(m%mod)%mod;///要先mod
    ll sqn = (ll)sqrt(n);
    ll blo = n/sqn;
    for1(i,1, min(blo,m) ){ ///m可能比blo小
        ans = ans - n/i*i;
        ans = quyu(ans,mod);
    }
    //printf("ans=%I64d\n",ans);
    if (m>blo){
        ll x = n/m + 1;///不完整的一块,eg. 500/256 = 1,则x=2,251~256特殊处理
        ans = quyu(ans-quyu(quyu(sum(n/x+1,m),mod)*(x-1),mod),mod);
        for1(i,x+1,sqn) ans = quyu(ans-sum(n/i+1,n/(i-1))*(i-1),mod);
    }
    //printf("ans=%I64d\n",ans);
    return (ans+mod)%mod;
}

int main()
{
    ll n,m;
    while (~scanf("%I64d %I64d",&n,&m)){
        if (n>m) printf("%I64d\n",f(n,m));
        else {
            //printf("%I64d\n",f(n,n-1));
            printf("%I64d\n",((m-n)%mod*(n%mod)%mod + f(n,n-1))%mod);
        }
    }
    return 0;
}

总结:

1.做不出跳题真的是个正确选择。

2.n!=sqrt(n)^2

3.BFS标记的时候想清楚,你要防止一个块因为扔进去的时候没有被标记导致被扔进去两次的情况,请在扔出来后判断有无标记或者扔进去的时候直接打标记。

4.数学题多推公式,找规律

 

你可能感兴趣的:(Codeforces补题)