校内集训(20170903)

T1:卡片(card)

【题目描述】

lrb喜欢玩卡牌。他手上现在有张牌,每张牌的颜色为红绿蓝中的一种。现在他有两种操作。一是可以将两张任意位置的不同色的牌换成一张第三种颜色的牌;二是可以将任意位置的两张相同颜色的牌换成一张该颜色的牌。两个操作后都可以将生成的牌放到任意位置。现在他想知道,最后一张牌可能是什么颜色的。

【输入描述】

第一行输入一个,表示卡牌数量。

第二行输入一个由’B’,’G’,’R’组成的长度为的字符串,分别表示卡牌的颜色为蓝色、绿色、红色中的一种。

【输出描述】

输出’B’,’G’,’R’中的若干个字母,按字典序输出。代表可能的最后一张牌的颜色。

【样例】

输入1

输出1

2

RB

G

输入2

输出2

3

GRG

BR

输入3

输出3

4

BBBB

B

【数据范围】

对于100%的数据,n<=200

——————————————————我是分割线————————————————————

这是一道简单分析题。(入门难度。。不要吐槽)

很显然我们发现如果3张牌都超过一张,三种情况都会出现(BGR)

那么如果有2张牌都超过2张,同上

剩下3种情况:

1,1,0(数字表示牌的数量)那么答案就是牌数是0的那张

2,1,0(2表示2张以上)那么答案就是牌数为1和0的两张

1,0,0(1表示一张以上)那么答案就是牌数为1的那一张

然后。。。做完了

下面贴上代码

#include<cstdio>
using namespace std;
int a[4],n;
int er,yi,sa;
char ch;
char ans[3]={'B','G','R'};
void swap(int &a,int &b){a^=b;b^=a;a^=b;}
int main(){
    freopen("card.in","r",stdin);
    freopen("card.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        ch=getchar();
        while(ch<'A'||ch>'Z')ch=getchar();
        if(ch=='B')a[1]++;
        else if(ch=='G')a[2]++;
        else if(ch=='R')a[3]++;
    }
    for(int i=1;i<=3;i++)if(a[i]>=2)er++;
    for(int i=1;i<=3;i++)if(a[i]>=1)yi++;    
    if(er>=2||yi==3)puts("BGR");
    else if(er==1&&yi==2){
        int x,y;
        for(int i=1;i<=3;i++)if(a[i]==0)x=i;else if(a[i]==1)y=i;
        if(x>y)swap(x,y);
        printf("%c%c
",ans[x-1],ans[y-1]);
    }else if(yi==2){
        int x;
        for(int i=1;i<=3;i++)if(!a[i])x=i;
        printf("%c
",ans[x-1]);
    }else if(yi==1){
        int x;
        for(int i=1;i<=3;i++)if(a[i])x=i;
        printf("%c
",ans[x-1]);
    }
    fclose(stdin);
    fclose(stdout);
}

————————————————我是分割线——————————————————

T2:取数(win)

【题目描述】

lrb目前有个数字,他想知道这个数中选出若干个数,平均数减中位数的最大值是多少。可以证明,对于一个递增数列,如果是平均数减中位数最大时的中位数,表示在两边分别取相邻数字的数量,表示以为中位数,在两侧各取相邻个数时平均数减中位数的值,那么为关于的单峰函数。

【输入描述】

第一行为,为数字个数。

第二行有个数,分别代表个数字。

【输出描述】

输出一个数,为平均数减中位数的最大值,保留两位小数。

【样例】

输入

输出

4

1 2 3 4

0.33

【数据范围】

对于60%的数据,n<=21

对于75%的数据,n<=1000

对于100%的数据,n<=10^5,0<=ai<=10^6

——————————————————我是分割线————————————————————

这题首先我们要想到的就是枚举中位数。

那么假设我们已知某一个数是中位数,那么我们怎么求最优的答案呢?

对于一个已经排序过的数列,

显然我们要在这个中位数的左边和右边同时取相同数量的数。

对于中位数的左边,显然越靠近中位数的数越优,

对于中位数的右边,越远离中位数的数越优。

所以对于第i个数作为中位数的情况,我们左边从第i-1位开始向左取数,右边从第n位开始向左取数。而取数的个数自然就是三分啦!(三分的过程用前缀和O(1)求平均数)

至于为什么偶数个数的数列不是最优解,这个我留个坑以后来填。

下面贴代码(当然我写了偶数版本)

#include<cstdio>
#include<algorithm>
#define MN 100005
using namespace std;
int a[MN],n;
long long sum[MN];
double ans=-100000000;
int main(){
    freopen("win.in","r",stdin);
    freopen("win.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    int l=0,r=0,lm=0,rm=0,le=0;
    double la=a[1],ra=a[1];
    for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
    for(int i=1;i<=n;i++){
        le=min(i-1,n-i);
        le=max(le,0);
        l=0,r=le;
        while(l<r-1){
            lm=l+(r-l)/3,rm=r-(r-l)/3;
            la=(double)(sum[i-1]-sum[i-1-lm]+sum[n]-sum[n-lm]+a[i])/(double)(2*lm+1);
            ra=(double)(sum[i-1]-sum[i-1-rm]+sum[n]-sum[n-rm]+a[i])/(double)(2*rm+1);
            if(la<ra)l=lm+1;
            else r=rm-1;
        }
        la=(double)(sum[i-1]-sum[i-1-l]+sum[n]-sum[n-l]+a[i])/(double)(2*l+1);
        ra=(double)(sum[i-1]-sum[i-1-r]+sum[n]-sum[n-r]+a[i])/(double)(2*r+1);
        if(la<ra)ans=max(ans,ra-(double)a[i]);
        else ans=max(ans,la-(double)a[i]);
    }
    la=ra=(double)(a[1]+a[2])/2.0;
    for(int i=1;i<n;i++){
        le=min(i-1,n-i-1);
        le=max(le,0);
        l=0,r=le;
        while(l<r-1){
            lm=l+(r-l)/3,rm=r-(r-l)/3;
            la=(double)(sum[i-1]-sum[i-1-lm]+sum[n]-sum[n-lm]+a[i]+a[i+1])/(double)(2*lm+2);
            ra=(double)(sum[i-1]-sum[i-1-rm]+sum[n]-sum[n-rm]+a[i]+a[i+1])/(double)(2*rm+2);
            if(la<ra)l=lm+1;
            else r=rm-1;
        }
        la=(double)(sum[i-1]-sum[i-1-l]+sum[n]-sum[n-l]+a[i]+a[i+1])/(double)(2*l+2);
        ra=(double)(sum[i-1]-sum[i-1-r]+sum[n]-sum[n-r]+a[i]+a[i+1])/(double)(2*r+2);
        if(la<ra)ans=max(ans,ra-(double)((double)(a[i]+a[i+1])/(double)2));
        else ans=max(ans,la-(double)((double)(a[i]+a[i+1])/(double)2));
    }
    printf("%.2lf
",ans);
    fclose(stdin);
    fclose(stdout);
}

 ————————————————我是分割线————————————————

T3:密码(key)

【题目描述】

lrb去柜员机取钱,并输入了他的四位密码。但是这一切都被一个黑帮拍摄了下来。幸好,这一次他的银行卡里并没有剩余任何钱。lrb知道了他的账户被异常登录后十分害怕,然后修改了他的密码。从此以后他为了不被看出密码,他开始“徐晃”。但这一切还是被拍摄了下来,拍摄多次以后,这个黑帮找到了你,希望你在一秒内帮他算出lrb的可能使用的密码数量。

【输入描述】

第一行输入一个,为lrb被拍摄的动作次数。

接下来行,每行第一个数为,表示lrb接下来的手指移动次数,接下来为一个长度为的数字串,表示lrb手指经过的轨迹。lrb手指经过的位置,可能没有按,也有可能按了多次。

【输出描述】

输出一行,为可能的密码数量。

【样例】

输入

输出

2

3 123

3 234

5

解释

lrb可能用的是2222,2223,2233,2333,3333五种密码

【数据范围】

设所有轨迹串的总长度为。

对于35%的数据,L<5000

对于100%的数据,1<=n<=1000,1<=t<=10^4,L<=10^6

——————————————————我是分割线——————————————————

这道题有一个简单思路(想不到的都是脑子抽了,比如我)

我们只要枚举所有的密码就好了

然后就是如何在O(n)时间内check一个密码

我们只要开一个数组记录从每一位开始,后面的第一个0-9分别在第几位,如果都能够找到,那么密码合法。

下面贴代码

#include<cstdio>
#define MN 1005
#define M 1000005
#define F(x,l,r) for(int x=l;x<=r;x++)
using namespace std;
char e[M];int f[M][10],ans,n,t[MN];
int main(){
    freopen("key.in","r",stdin);
    freopen("key.out","w",stdout);
    scanf("%d",&n); 
    for(int i=1;i<=n;i++)scanf("%d%s",&t[i],e+t[i-1]),t[i]+=t[i-1];
    for(int i=0;i<=9;i++)f[t[n]][i]=t[n];
    for(int i=t[n]-1;i>=0;i--){for(int j=0;j<=9;j++)f[i][j]=f[i+1][j];f[i][e[i]-'0']=i;}
    F(a,0,9)
        F(b,0,9)
            F(c,0,9)
                F(d,0,9){
                    int tot=0;
                    F(i,0,n-1)if(f[f[f[f[t[i]][a]][b]][c]][d]<t[i+1])tot++;
                    ans+=tot==n;
                }
    printf("%d
",ans);
}
原文地址:https://www.cnblogs.com/ghostfly233/p/7473367.html