20170903校内训练

卡片(card)

【题目描述】

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

【输入描述】

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

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

【输出描述】

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

【样例】

输入1

输出1

2

RB

G

输入2

输出2

3

GRG

BR

输入3

输出3

4

BBBB

B

【数据范围】

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

16个if。。。

首先如果只有某种颜色,那么剩下的必然只有这种颜色。

其次如果只有两张牌,那么直接判断出另一张牌即可。

剩下的情况,如果某种颜色没牌,另一种颜色只有一张牌,那么最后剩下的为这两种颜色。

除了以上的情况,剩下三种牌都有可能。

#include<iostream>
#include<cstdio>
using namespace std;
char c[1000];int r=0,g=0,b=0;
int main()
{
    //freopen("card.in","r",stdin);freopen("card.out","w",stdout);
    int n;scanf("%d",&n);
    if(n==0){return 0;}
    scanf("%s",c);
    for(int i=0;i<n;i++)
    {
        if(c[i]=='R')r++;
        if(c[i]=='G')g++;
        if(c[i]=='B')b++;
    }
    if(r>=1&&g>=1&&b>=1)puts("BGR");
    else if(r>=2&&g>=2)puts("BGR");
    else if(r>=2&&b>=2)puts("BGR");
    else if(g>=2&&b>=2)puts("BGR");
    else if(r>=2&&g>=1)puts("BG");
    else if(r>=2&&b>=1)puts("BG");
    else if(g>=2&&r>=1)puts("BR");
    else if(g>=2&&b>=1)puts("BR");
    else if(b>=2&&r>=1)puts("GR");
    else if(b>=2&&g>=1)puts("GR");
    else if(b>=1&&g>=1)puts("R");
    else if(b>=1&&r>=1)puts("G");
    else if(g>=1&&r>=1)puts("B");
    else if(b>=1)puts("B");
    else if(g>=1)puts("G");
    else if(r>=1)puts("R");
    return 0;
}
View Code

取数(win)

【题目描述】

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

【输入描述】

第一行为n,为数字个数。

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

【输出描述】

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

【样例】

输入

输出

4

1 2 3 4

0.33

【数据范围】

对于60%数据,n<=21

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

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

排序,首先取奇数个数显然不会比取偶数个数劣。证明:设中间两个数为x1,x2(x1<x2),其它数的总和为x3,总个数为n,我们可以通过删除x2使得结果不劣

设S1为删除前平均数-中位数,S2为删除后平均数-中位数,则S1=(x1+x2+x3)/n-(x1+x2),S2=(x1+x3)/(n-1)-x1

S2-S1=(nx1+xn3-(n-1)x1-(n-1)x2-(n-1)x3)/(n(n-1))-x1+x1+x2

          =(x1-nx2+x2+x3+n(n-1)x2)/(n(n-1))

          =(x1+x3+x2(n^2-2n+1))/(n(n-1))

          =(x1+(n-1)^2*x2+x3)/(n(n-1))

因为x1+(n-1)^2*x2+x3>=0,(n(n-1))>0  (因为n>=2)

所以S2-S1>=0,S2>=S1,所以取奇数个数

我们取的数一定是如下图所示(黑圈是中位数,黑线是取的数,红线是题目给出的数据)

如果不这么取,那么平均数会变小。我们可以枚举中位数。为了快速算出黑线的数的总和,我们需要前缀和

设两条黑线长度都为L(即取L个数),f(L)为上图黑线和黑圈的平均数

因为总是有一个临界值(设取了x个数),使得再取第(x+1)个数对平均数的贡献为负数(因为数据单调不降(之前排好序了))

所以f(L)是一个上单峰函数,我们可以通过三分法求得f(L)的峰值

由于这道题的所有数加起来会爆int,所以我们要用long long

这样,我们可以在O(nlog(n))的时间内解决

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int a[100101];long long b[100101];double ans=-1.0;int n;
double f(int x,int l)//求以x为中位数,l为黑线长度的平均数减中位数的值
{
    return (double)(b[x]-b[x-l-1]+b[n]-b[n-l])/(l*2+1)-a[x];
}
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);
    for(int i=1;i<=n;i++)b[i]=b[i-1]+a[i];
    for(int i=1;i<=n;i++)
    {
        int l=0,r=min(i-1,n-i),o;long long Max=-1ll;
        while(1)
        {
            if(r-l<5)
            {
                for(int j=l;j<=r;j++)ans=max(ans,f(i,j));
                break;
            }
            int lm=l+(r-l)/3,rm=r-(r-l)/3;
            if(f(i,lm)<f(i,rm))l=lm;
            else r=rm;
        } 
    }
    printf("%.2lf",ans);
    return 0;
}
View Code

密码(key)

【题目描述】

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

【输入描述】

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

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

【输出描述】

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

【样例】

输入

输出

2

3 123

3 234

5

解释

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

【数据范围】

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

对于35%的数据,L<=5000

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

首先可以在O(10L)的时间内预处理出对于每一位向后的第一个某个数字的位置。我们可以把轨迹串拼在一起

用f[i][j]表示从第i个位置(包括i(因为可以多次按同一个键))向后查找j数(0~9)的位置,不存在设为1000001,再令f[1000001][0~9]=10000001

然后就可以暴力枚举每种密码后用的O(n)时间进行check,从第0号位置开始。用f[0][第一位的数]跳到下一个位置,接着用f[刚刚跳到的位置][第二位的数]继续跳,以此类推。

最后把结果不是1500000的统计进答案即可。

#include<iostream>
#include<cstdio>
using namespace std;
char c[1000010];int t[1000010],f[1000010][10],ans=0;
int main()
{
//    freopen("key.in","r",stdin);freopen("key.out","w",stdout);
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%s",&t[i],c+t[i-1]);t[i]+=t[i-1];
    }
    for(int i=0;i<=9;i++)f[1000001][i]=1000001;
    for(int i=n;i>=1;i--)
    for(int j=t[i]-1;j>=t[i-1];j--)
        if(j==t[i]-1)
        {
            for(int k=0;k<=9;k++)f[j][k]=1000001;
            f[j][c[j]-'0']=j;
        }
        else
        {
            for(int k=0;k<=9;k++)f[j][k]=f[j+1][k];
            f[j][c[j]-'0']=j;
        }
    for(int a=0;a<=9;a++)
    for(int b=0;b<=9;b++)
    for(int c=0;c<=9;c++)
    for(int d=0;d<=9;d++)
    {
        bool ok=1;
        for(int i=1;i<=n;i++)
        {
            if(f[f[f[f[t[i-1]][a]][b]][c]][d]==1000001){ok=0;break;}
        }
        if(ok)ans++;
    }
    printf("%d",ans);
    return 0;
}
View Code
posted @ 2017-09-03 20:52  lher  阅读(196)  评论(0编辑  收藏  举报