[2019.3.8]codeforces1138 Codeforces Round #545 (Div. 2)

概况

排名:47/3403

过题数:4

Rating:\(\color{green}{+118}\)(\(\color{purple}{1910}\))

(上\(\color{purple}{紫}\)啦!)

题目

A. Sushi for Two

AC时间:3min,496分

题解:

每次记录相同的连续长度,和上一个长度取个min,更新答案就好了。

code:

#include<bits/stdc++.h>
using namespace std;
int n,a[100010],ans,tot,lst;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]),a[i]!=a[i-1]?ans=max(ans,min(tot,lst)),lst=tot,tot=0:0,++tot;
    ans=max(ans,min(lst,tot));
    printf("%d",ans*2);
    return 0;
}

B. Circus

AC时间:33min,845分(-1)

B题想了半小时。。。我以为我完了。。。

题解:

\(c_i=1,a_i=0\)为第一种,数量为\(tot_1\);\(c_i=1,a_i=1\)为第二种,数量为\(tot_2\);\(c_i=0,a_i=1\)为第三种,数量为\(tot_3\);\(c_i=0,a_i=0\)为第四种,数量为\(tot_4\)

我们枚举第一场中第一种,第二种的数量,发现剩下两种的\(c_i=0\),所以第一场有几个会的就确定了,那么第二场有几个会的也确定了,由于剩下的第二种人必然会在第二场,也就是必然会对第二场会的人数做出贡献,所以第二场中有几个第三种人也确定了,那么第一场有几个第三种人也确定了,又因为第一场中必然有\(\frac{n}{2}\)个人,那么第一场中第四种人的数量也随之确定。

然后判断方案合法性即可。

code:

#include<bits/stdc++.h>
using namespace std;
int n,c[5010],a[5010],tot1,tot2,tot3,tt1,tt2,tt3,tt4,t1,t2;
void scan(int &x){
    x=getchar();
    while(!isdigit(x))x=getchar();
    x-='0';
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scan(c[i]);
    for(int i=1;i<=n;++i)scan(a[i]),c[i]?(a[i]?++tot2:++tot1):tot3+=a[i];
    for(int i=0;i<=tot1;++i){
        for(int j=0;j<=tot2&&i+j<=n/2;++j){
            t1=i+j,t2=tot2-j;
            if(t1<t2)continue;
            tt1=i,tt2=j,tt3=tot3-(t1-t2),tt4=n/2-tt1-tt2-tt3;
            if(tt1>tot1||tt2>tot2||tt3>tot3||tt4>n-tot1-tot2-tot3)continue;
            if(tt3>=0&&tt4>=0)goto ANS;
        }
    }
    puts("-1");
    return 0;
    ANS:
    for(int i=1;i<=n;++i){
        if(c[i]&&!a[i]&&tt1)printf("%d ",i),--tt1;
        if(c[i]&&a[i]&&tt2)printf("%d ",i),--tt2;
        if(!c[i]&&a[i]&&tt3)printf("%d ",i),--tt3;
        if(!c[i]&&!a[i]&&tt4)printf("%d ",i),--tt4;
    }
    return 0;
}

C. Skyscrapers

AC时间:49min,1476分

题解:

比B水得多。

记第\(i\)行第\(j\)列的元素为\((i,j)\)

记录\(rnkr_{i,j}\)表示\((i,j)\)是第\(i\)行中第几小的(即它将在第\(i\)行中获得的编号),\(rnkc_{i,j}\)表示\((i,j)\)是第\(j\)列中第几小的(即它将在第\(j\)列中获得的标号)。

再记录\(mxr_{i}\)表示第\(i\)行中有多少不同的元素(即第\(i\)行中最大的元素是第几小的),\(mxc_i\)表示第\(i\)列中有多少不同的元素(即第\(j\)列中最大的元素是第几小的)。

那么显然,我们考虑第\(i\)行第\(j\)列的时候,\((i,j)\)获得的编号将是\(max(rnkr_{i,j},rnkc_{i,j})\)

我们设\((i,j)\)获得的编号为\(id\)

那么为了满足相对大小不变,第\(i\)行中比\((i,j)\)大的元素的编号将增大\(id-rnkr_{i,j}\),则第\(i\)行中最大的编号将是\(mxr_i+(id-rnkr_{i,j})\);

同理,为了满足相对大小不变,第\(j\)列中比\((i,j)\)大的元素的编号将增大\(id-rnkc_{i,j}\),则第\(j\)列中最大的编号将是\(mxc_j+(id-rnkc_{i,j})\)

于是答案就是上面两个东西的较大值了。

code:

#include<bits/stdc++.h>
using namespace std;
struct CMP{
    int id,v;
}c[1010];
int n,m,a[1010][1010],rnkr[1010][1010],rnkc[1010][1010],mxr[1010],mxc[1010],t;
bool cmp(CMP x,CMP y){
    return x.v<y.v;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)scanf("%d",&a[i][j]);
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j)c[j]=(CMP){j,a[i][j]};
        sort(c+1,c+m+1,cmp);
        for(int j=1;j<=m;++j)c[j].v>c[j-1].v?++mxr[i]:0,rnkr[i][c[j].id]=mxr[i];
    }
    for(int i=1;i<=m;++i){
        for(int j=1;j<=n;++j)c[j]=(CMP){j,a[j][i]};
        sort(c+1,c+n+1,cmp);
        for(int j=1;j<=n;++j)c[j].v>c[j-1].v?++mxc[i]:0,rnkc[c[j].id][i]=mxc[i];
    }
    for(int i=1;i<=n;++i){
        for(int j=1;j<=m;++j){
            t=max(rnkr[i][j],rnkc[i][j]);
            printf("%d ",max(mxr[i]+(t-rnkr[i][j]),mxc[j]+(t-rnkc[i][j])));
        }
        puts("");
    }
    return 0;
}

D. Camp Schedule

AC时间:1h24min,1413分(-1)

题解:

发现一定是贪心地一个一个地排\(t\),直到不能排为止。

但是前一个\(t\)和后一个\(t\)可能会有重叠部分。

重叠部分最多是多少呢?

就是\(t\)的border,即\(t\)最长的前缀等于后缀的部分啦。

border怎么求呢?

跑一遍kmp就好啦。

然后呢?

我们以一个\(t\)串为基础,将\(t\)的border以后的部分不停地加入,直到不能再加就好了。

什么时候不合法呢?

1.剩下的长度不够放了;

2.放完后串的1的数量大于\(s\)中1的数量,或者0的数量大于\(s\)中的0数量。

注意:有可能连一个\(t\)也放不下,即\(t\)的长度大于\(n\),或\(t\)中1的数量大于\(s\)中1的数量,或者是\(t\)中0的数量大于\(s​\)中0的数量。

code:

#include<bits/stdc++.h>
using namespace std;
char s[500010],t[500010],p[500010];
int n,m,t1,tmp,nw,nxt[500010],len;
void getnxt(){
    nxt[1]=0;
    nw=0;
    for(int i=2;i<=n;i++){
        while(nw!=0&&t[nw+1]!=t[i])nw=nxt[nw];
        if(t[nw+1]==t[i])nxt[i]=++nw;
        else nxt[i]=nw;
    }
}
int main(){
    scanf("%s%s",s+1,t+1);
    n=strlen(s+1),m=strlen(t+1);
    if(m>n)return(printf("%s",s+1),0);
    getnxt(),len=nxt[m];
    for(int i=1;i<=n;++i)t1+=s[i]=='1';
    for(int i=1;i<=m;++i)tmp+=t[i]=='1';
    if(tmp>t1)return(printf("%s",s+1),0);
    if(n-m+tmp<t1)return(printf("%s",s+1),0);
    for(int i=1;i<=m;++i)p[i]=t[i];
    t1-=tmp,tmp=0,nw=len+1;
    for(int i=1+len;i<=m;++i)tmp+=t[i]=='1';
    for(int i=m+1;i<=n;++i,nw=(nw==m?len+1:nw+1)){
        if(t[nw]=='0'){
            if(n-i+1==t1){
                for(int j=i;j<=n;++j)p[j]='1';
                break;
            }else p[i]='0';
        }else{
            if(!t1){
                for(int j=i;j<=n;++j)p[j]='0';
                break;
            }else p[i]='1',--t1;
        }
    }
    printf("%s",p+1);
    return 0;
}

总结

本场难度:E>F>D>B>C>A

下一场打Div.1就会掉回\(\color{blue}{蓝}\)名了QWQ

转载于:https://www.cnblogs.com/xryjr233/p/CF1138.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值