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; }
取数(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; }
密码(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; }