2018提高组模拟6
T1
———————————————————————————————————————20180922
古代密码
描述
古罗马帝国有一个拥有各种部门的强大政府组织。其中一个部门就是保密服务部门。为了保险起见,在省与省之间传递的重要文件中的大写字母是加密的。当时最流行的加密方法是替换和重新排列。
替换方法是将所有出现的字符按照一个规则替换,比如ABCDEFGHIJKLMNOPQRSTUVWXYZ到BCDEFGHIJKLMNOPQRSTUVWXYZA,如果原词是 "VICTORIOUS" 则它变成 "WJDUPSJPVT"。
排列方法改变原来单词中字母的顺序。例如:将顺序<2, 1, 5, 4, 3, 7, 6, 10, 9, 8>应用到 "VICTORIOUS" 上,则得到"IVOTCIRSUO"。
人们很快意识到单独应用替换方法或排列方法加密,都是很不保险的。但是如果结合这两种方法,在当时就可以得到非常可靠的加密方法。所以,很多重要信息先使用替换方法加密,再将加密的结果用排列的方法加密。用两种方法结合就可以将"VICTORIOUS" 加密成"JWPUDJSTVP"。
考古学家最近在一个石台上发现了一些信息。初看起来它们毫无意义,所以有人设想它们可能是用替换和排列的方法被加密了。人们试着解读了石台上的密码,现在他们想检查解读的是否正确。他们需要一个计算机程序来验证,你的任务就是写这个验证程序。
输入
输入有两行。第一行是石台上的文字。文字中没有空格,并且只有大写英文字母。第二行是被解读出来的加密前的文字。第二行也是由大写英文字母构成的。
两行字符数目的长度都不超过100。
输出
如果第二行经过某种加密方法后可以产生第一行的信息,输出 "YES",否则输出"NO"。
样例输入
JWPUDJSTVP
VICTORIOUS
样例输出
YES
提示
对于30%的数据:字符串长度<=10
对于50%的数据:字符串长度<=50
对于100%的数据:字符串长度<=100
题解
最容易被坑的就是这道题的替换规则是不定的!!!!!!!!
比如A变C,B变R,C变A……
但唯一可以确定的是每一个大写字母只对应另一个替换后的字母。
所以我们只需统计一下每个字母出现了多少次,再排一下序,比较一下就OK了 。
^__^
#include<bits/stdc++.h>
using namespace std;
char a[110],b[110];
int a1[30],a2[30];
int main(){
scanf("%s%s",a+1,b+1);
int len=strlen(a+1),len1=strlen(b+1);
if(len!=len1){
printf("NO");
return 0;
}
for(int i=1;i<=len;i++){
a1[a[i]-'A'+1]++;
a2[b[i]-'A'+1]++;
}
sort(a1+1,a1+27);
sort(a2+1,a2+27);
for(int i=1;i<=26;++i)
if(a1[i]!=a2[i]){
printf("NO");
return 0;
}
printf("YES");
return 0;
}
T2
塔
描述
小A想搭一个体积不超过m的塔,他有各种大小的立方积木,比如边长为a的积木,体积为a^3,现在小A需要你给一个X,每次小A会用一个体积不超过X的最大积木,依次到搭好为止,现在他想最大化积木的个数,同时在积木个数最大的情况下使X最大
输入
一行一个数m
输出
一行两个数,最多积木数以及x
样例输入
48
样例输出
9
42
【样例解释】 X=23或42都是9次,42 = 3^3 + 2^3 + 7*1^3
【数据范围】
30%:m<=10^5
50%:m<=10^10
100%:m<=10^15
题解
题意有些不好理解:计算x由多少个立方数拼起,再在m的范围内求最大的拼数数的x;
找到最大的 a 使得 a^3 不超过 m,
接下来 X 的第一块积木必然为 a 或 a-1
1.用 a,m2=m-a^3
2.用 a-1, X 最大为 a^3-1, m2 = a^3-1-(a-1)^3=3a^2-3a
3.用 a-2, X 最大为(a-1)^3-1,m2=(a-1)^3-1-(a-2)^3=3a^2-9a+6
显然 2 一定比 3 优
所以就可以不用考虑3及其后面的了。
用神奇而又莫名快速的递归就能完成了。
#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
#define ll long long
ll m,ans,pos;
void dfs(ll x,ll p,ll n){
if(!x){
if(n>ans||(n==ans&&p>pos)){
ans=n;
pos=p;//cout<<5;
}
return;
}
ll a=floor(pow(x,1.0/3));//floor向下取整 开立方
dfs(x-a*a*a,p+a*a*a,n+1);
dfs(3*a*a-3*a,p+(a-1)*(a-1)*(a-1),n+1); //
}
int main(){
scanf("%lld",&m);
dfs(m,0,0);
printf("%lld %lld",ans,pos);
return 0;
}
T3
数球
描述
小A有n个球,编号分别为1到n,小A每次都会从n个球中取出若干个球,至少取一个,至多取n个,每次取完再放回去,需要满足以下两个条件。
(1)每次取出的球的个数两两不同。
(2)每次取出的球的集合两两不包含。
包含是指,对于两次取球,对于取的数目少的那次取球的所有球都出现在取的数目多的那次取球中,例如{1,2}和{1,2,4},{1,2}和{2,3}则不算作包含。
而小A现在突然想知道他最多能进行多少次这样的操作,并希望你能给出具体的取球方案
输入
一个整数n。
输出
第一行一个数k,表示能进行的最多次数。
接下来k行,每行第一个整数p,表示这次取的球数,接下来p个数表示这次取的球的编号,编号只需要不同,不需要按照顺序输出,本题设有spj。
对于每个测试点,每组数据第一行正确可以获得20%的分,如果第一行和方案均正确获得100%的分。
样例输入
4
样例输出
2
1 1
2 3 4
提示
对于30%的数据,n<=7。
对于50%的数据,n<=20。
对于70%的数据,n<=100。
对于100%的数据,4<=n<=1000。
题解
手算30%的数据,其实也就4到7,也就4种
算到十几的时候,你不难发现可以取n-2次,当然,也有证明的:
总共有 n 种大小的 子集,大小为 n 的肯定不能选,因为必然包含大小为 1 的子集。
大小 为 n-1 的也不能选,因为选的话,那么大小为 1 的必然不能在大小为 n-1 的子集里,
大小为 2 的不能全在大小为 n-1 的子集里,这样大小 为 1 的必然被大小为 2 的包含,答案至多为 n-2
总数解决了,我们就来看方案了。
方法一:
奇偶分别计算
如5到7:
5:
1
2 3
3 4 5
先每一个加7
1 7
2 3 7
3 4 5 7
再加两组:
6
1 2 3 4 5
就变成7:
1 7
2 3 7
3 4 5 7
6
1 2 3 4 5
就大功告成!
方法二:
这次就不用隔一个算一个,直接往下推就好了。
如5到6:
5:
1
2 3
3 4 5
先将所有取反:(即有的去掉,没的加上)
2 3 4 5
1 4 5
1 2
然后再加一个:
6
所以6:
2 3 4 5
1 4 5
1 2
6
不过,方法二要用bitset取反,所以还是方法一较简单,也很好写。
#include<bits/stdc++.h>
using namespace std;
int n;
vector<int>a[1010];
int main(){
scanf("%d",&n);
if(n%2==0){
a[1].push_back(1);
a[2].push_back(2);
a[2].push_back(3);//4
for(int i=6;i<=n;i+=2){
for(int j=1;j<=i-4;j++)
a[j].push_back(i);
a[i-3].push_back(i-1);
for(int j=1;j<=i-2;j++)
a[i-2].push_back(j);
}
}
else{
a[1].push_back(1);//3
for(int i=5;i<=n;i+=2){
for(int j=1;j<=i-4;j++)
a[j].push_back(i);
a[i-3].push_back(i-1);
for(int j=1;j<=i-2;j++)
a[i-2].push_back(j);
}
}
printf("%d",n-2);
for(int i=1;i<=n-2;i++){
printf("\n%d",a[i].size());
for(int j=0;j<a[i].size();j++)
printf(" %d",a[i][j]);
}
return 0;
}