2017.5.15 COCI2011/2012 Contest#5

A KRIŽALJKA

【分析】
这一题只要仔细看题目的话应该是简单的,也相信大家不是来看我怎么花式讲水题的,就直接贴代码了。

【代码】

#include 
using namespace std;
#define M 40
char a[M],b[M];
int n,m,f=1;
int pos1,pos2;
char mp[M][M];
int main(){
    scanf("%s",a+1);
    scanf("%s",b+1);
    n=strlen(a+1);
    m=strlen(b+1);
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++)mp[i][j]='.';
    }
    for(int i=1;f&&i<=n;i++){
        for(int j=1;f&&j<=m;j++){
            if(a[i]==b[j]){
                f=0;
                pos1=j;
                pos2=i;
            }
        }
    }
    for(int i=1;i<=n;i++)mp[pos1][i]=a[i];
    for(int i=1;i<=m;i++)mp[i][pos2]=b[i];
    for(int i=1;i<=m;i++){
        for(int j=1;j<=n;j++)printf("%c",mp[i][j]);
        puts("");
    }
    return 0;
}

B EKO

【分析】
考虑到答案存在单调性,所以就直接二分咯。
想要1A的话还是要注意算md的时候有可能爆int。

【代码】

#include 
using namespace std;
#define ll long long
#define M 1000005
int a[M];
int n,m;
int l,r;
int res;
bool chk(int h){
    ll hav=0;
    for(int i=1;i<=n;i++){
        if(a[i]<=h)continue;
        hav+=a[i]-h;
    }
    return hav>=m;
}
int main(){
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        r=max(r,a[i]);
    }
    while(!(l>r)){
        int md=1ll*(l+r)/2;
        if(chk(md)){
            res=md;
            l=md+1;
        }else r=md-1;
    }
    printf("%d\n",res);
    return 0;
}

C DNA

【分析】
知道题可以考虑贪心和dp,但是,我并没有找出容易的贪心做法,那就换一个路子,dp。
我们定义dp[i][j]表示将1~i全部变成j需要的最小个数。
那么转移方程也就比较容易推出了。
但是比赛的时候没有将这题1A,原因是RE了…

【代码】

#include
using namespace std;
#define M 1000005
int dp[2][M];//B 0;A 1 
bool mk[M];
char a[M];
int n;
int main(){
    scanf("%d",&n);
    scanf("%s",a+1);
    for(int i=1;i<=n;i++){
        bool f=(a[i]=='A');
        dp[f][i]=min(dp[f][i-1],dp[!f][i-1]+1);
        dp[!f][i]=min(dp[f][i]+1,dp[!f][i-1]+1);
    }
    printf("%d",dp[1][n]);
    return 0;
}

D RAZBIBRIGA

【分析】
花了半天时间看英文题似懂非懂。感谢小C的中文题意。
我们发现,想要组成这样一个首尾相连的矩形,唯一有用的只是一个单词的开头和结尾的字母。
既然有四个起点和终点,那么我们就枚举终态。
不过还要考虑到一些重复算的相同的部分。如何将他们去掉,就看大家各显神通了。

【代码】

#include 
using namespace std;
#define M 100005
#define ll long long
char a[M][15];
int n,m;
ll ans;
int cnt[28][28];
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%s",a[i]+1);
    m=strlen(a[1]+1);
    for(int i=1;i<=n;i++){
        int x=a[i][1]-'A';
        int y=a[i][m]-'A';
        cnt[x][y]++;
    }
    for(int i=0;i<26;i++){
        for(int j=0;j<26;j++){
            for(int k=0;k<26;k++){
                for(int p=0;p<26;p++){
                    int you[28][28];
                    memset(you,0,sizeof(you));
                    ll now=cnt[i][j];
                    you[i][j]++;
                    now*=1ll*(cnt[j][k]-you[j][k]);
                    you[j][k]++;
                    now*=1ll*(cnt[p][k]-you[p][k]);
                    you[p][k]++;
                    now*=1ll*(cnt[i][p]-you[i][p]);
                    if(now<=0)continue;
                    ans+=now;
                }
            }
        }
    }
    cout<return 0;
}

E BLOKOVI

【分析】
比赛的时候完全没什么想法。直接敲了30分。
比赛结束后听jiedai大神的思路,感觉智商又下线了。
啦啦啦,我又回来了!

以下是sonytoy大神的证明

**我们假设已知当前积木 i 质量 mass ,之前所有积木质量M,
以积木 i 的左端点为原点,设 i1 块积木构成的整体的重心在X轴的坐标为 x , x[0,2]
新的重心横坐标
NewCentre=Mx+1massM+mass=Mx+massM+mass
新的最右端
Right=Max(2,x+dp[i1])
所以
dp[i]=Max(2,x+dp[i1])Mx+massM+mass
=Max(2M+2mass,x(M+mass)+dp[i1](M+mass))MxmassM+mass
=Max((2x)M+mass,(x1)mass+dp[i1](M+mass))M+mass
可以发现, Max() 中的两个式子,一个是单调递增,一个是单调递减的,均满足单调性,因此直接考虑 x=0,2 完全是可以的,换句话说,即一个是重心在左端点,一个是重心在右端点。所以答案是在这两个地方产生的。**

【代码】

#include 
using namespace std;
#define db double
#define M 300005
db ans,sum;
int a[M];
int n;
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=n;i>1;i--){
        sum+=a[i];
        db Z=1.0*a[i]/sum;
        ans=max(ans,max(ans+Z,2-Z));
    }
    printf("%.8f\n",ans);
    return 0;
}

F POPLOČAVANJE

【分析】
这题十分的奇怪,因为纯暴力可以有58分。这可是最后一道题啊。
关于字符串的题目,博主现在只有这几种方法:Hash、Trie树。因为我还没有对字符串继续深入学。
所以除了暴力,我还想到了Hash,可是这样复杂度就是 O(nm) ,这样应该会超时。但是 很奇怪 过掉了。(加一些奇奇怪怪的优化 跑的居然比正解的AC自动机、后缀自动机还快)。
但我觉得不能超过正解,所以我还是将原始的代码贴出来。

【代码】

#include
#define usi unsigned int
#define ll long long
using namespace std;
#define M 305005
#define P 233
char a[M],b[5005];
usi num[M],F[M];
int n,m,ans;
int mk[M];
ll cnt;
int main(){
    scanf("%d %s %d",&n,a+1,&m);

    F[0]=1;
    for(int i=1;i<=n;i++)F[i]=F[i-1]*P;
    for(int i=1;i<=n;i++)num[i]=num[i-1]*P+a[i];

    while(m--){
        scanf("%s",b+1);
        int len=strlen(b+1);
        usi res=0;
        for(int i=1;i<=len;i++)res=res*P+b[i];
        int w=n-len+1;
        for(int i=1;i<=w;i++){
            if(res==num[i+len-1]-num[i-1]*F[len])mk[i]++,mk[i+len]--;
        }
    }
    for(int i=1;i<=n;i++)cnt+=mk[i],ans+=(!cnt);
    printf("%d\n",ans);
    return 0;
}

这场比赛做的还是比较顺利的。两题1A,两题较快A掉,还有最后两题,花了不少时间。而且F题被一个学弟给血虐了……我感觉这其实暴露了一些问题:不是说他们天赋异禀,而是说他们比我们更踏实,不在意那些所谓的题数,而是把事情做精。虽然比他们的年龄大,但是还是要向他们学习。

你可能感兴趣的:(2017.5.15 COCI2011/2012 Contest#5)