D - Make a Power of Two
题目关键信息: 随便删除,只能在右边加,前导零不自动删除
- 因为要看看到底操作几次就可以变得和 2 k ( 0 ≤ k ≤ 63 ) 2^k\ \ (0\le k\le 63) 2k (0≤k≤63)相等,拿到题我们就先想暴力一点的做法,判断时间复杂度,再考虑优化.所以最暴力的就是直接枚举 2 k 2^k 2k,再与 n n n来作比较,看看需要操作几次
- 计算时间复杂度: 1 0 4 ∗ 63 ∗ 9 10^4*63*9 104∗63∗9< 1 0 8 − 9 10^{8-9} 108−9,所以行.
- 然后再来考虑怎么把 n n n与 2 k 2^k 2k进行比较,来计算需要几个操作,这个其实就是字符串匹配,将 2 k 2^k 2k来匹配原来的 n n n,因为只能从左边添加字符,假设 2 k = 1024 2^k=1024 2k=1024所以 n n n中必须是有从 1 1 1到 4 4 4连续且有顺序排列的才行,举个例子: n = 1052 n=1052 n=1052,匹配成功了 3 3 3个, n = 2052 n=2052 n=2052,匹配成功 0 0 0个,因为必须要删除完所有的才能加入 1 1 1.
- 再考虑一点小小的优化和怎么写才好写
- 将 2 k 2^k 2k预处理出来,放在一个数组里面方便每次用,减少时间复杂度.
- 将 n n n和 2 k 2^k 2k都转换为字符串,方便处理.
- 设 n n n的字符串下长度为 L e n Len Len,匹配成功了 n o w now now个(注意, n o w now now指向的是下一个位置,所以要 n o w − − now-- now−−),当前 2 k 2^k 2k的长度是 l e n [ i ] len[i] len[i].最终的答案就是 l e n [ i ] − n o w + L e n − n o w len[i]-now+Len-now len[i]−now+Len−now,其中 l e n [ i ] − n o w len[i]-now len[i]−now是 n n n需要添加的, L e n − n o w Len-now Len−now是需要删除的.
注意:需要枚举到 2 63 2^{63} 263才行
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn
#define int long long
using namespace std;
int n,T,a[100],len[100];
char b[100][100],s[100];
signed main(){
int base=63;
scanf("%lld",&T); a[1]=1;
for(int i=2;i<=base;i++) a[i]=a[i-1]*2;
for(int i=1;i<=base;i++){
while(a[i]){
b[i][++len[i]]=a[i]%10+'0';
a[i]/=10;
}
reverse(b[i]+1,b[i]+1+len[i]);
}
while(T--){
scanf("%s",s+1); int Len=strlen(s+1);
int ans=10000;
for(int i=1;i<=base;i++){
int now=1;
for(int j=1;j<=Len;j++){
if(s[j]==b[i][now]) now++;
}
now--;
ans=min(ans,len[i]-now+Len-now);
}
printf("%lld\n",ans);
}
return 0;
}
E - Polycarp and String Transformation
首先思考一下给出的字符串那么长,到底该怎么去分割开,要是分割开,那就爽歪歪.
- 以 e v e r y w h e r e v r y w h r v r y h r v r h r v h v everywherevrywhrvryhrvrhrvhv everywherevrywhrvryhrvrhrvhv为例子
- 正常分割 e v e r y w h e r e v r y w h r v r y h r v r h r v h v everywhere \ \ vrywhr \ \ vryhr \ \ vrhr \ \ vh \ \ v everywhere vrywhr vryhr vrhr vh v
所以不难看到,要想把他分出来还是有点难度的,但是每次会删除一个字符,我们再倒过来看看,先是只有一种字母,然后再是只有两种字符……有全部的字母,所以我们倒序枚举,从字符串的末尾开始向头开始枚举的话,就可以找到删除的顺序,因为后删除的字符肯定在后面还会出现的,而先删除的字符就只会在前面,会后被枚举到,因此,我们找到了删除的顺序
再来考虑原字符串是什么,先这样,我们统计一下每个字母在大字符串出现了多少次,结果是这样的:
e = 4 e=4 e=4, w = 2 w=2 w=2, y = 3 y=3 y=3, r = 8 r=8 r=8, h = 5 h=5 h=5, v = 6 v=6 v=6.
每个字符在每次轮回的时候出现次数都是一样的,在删除了它之前是一个特定值 x x x,删除后就是 0 0 0,所以我们将所有的字符出现次数除上它是第几个被删除掉的,结果就是这样了
e = 4 e=4 e=4, w = 1 w=1 w=1, y = 1 y=1 y=1, r = 2 r=2 r=2, h = 1 h=1 h=1, v = 1 v=1 v=1
然而母串,也就是原字符串中,每个字母出现次数也是也么多次.
至此,我们已经求出了字符串和顺序,要考虑 − 1 -1 −1就简单了,就是模拟题目所说过程,用我们得到的字符串去看,行不行就好了
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define maxn 600000
#define int long long
using namespace std;
int n,T,shunxu[maxn],book[maxn],cnt[maxn],k,flag,anslen;
char s[maxn],Ans[maxn],ans[maxn],Anss[maxn];
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%s",s+1); int len=strlen(s+1);
for(int i=len;i;i--){
cnt[s[i]]++;
if(book[s[i]]) continue;
book[s[i]]=true;
shunxu[++k]=s[i];
}
reverse(shunxu+1,shunxu+1+k);
for(int i=1;i<=k;i++) cnt[shunxu[i]]/=i;
for(int i=1;i<=len;i++){
cnt[s[i]]--;
if(cnt[s[i]]==-1){
for(int j=1;j<i;j++) ans[j]=Ans[j]=Anss[j]=s[j];
anslen=i-1;
break;
}
}
for(int i=1,now=1,nowk=1,nowlen=anslen;i<=len;i++){
if(i==len && now) now;
if(now>nowlen){
nowlen=0;
memset(Anss,0,sizeof Anss);
for(int j=1;j<=anslen;j++){
if(Ans[j]==shunxu[nowk]) Ans[j]='*';
if(Ans[j]=='*') continue;
Anss[++nowlen]=Ans[j];
}
nowk++; now=1;
}
if(s[i]!=Anss[now]) {flag=true; break;}
now++;
}
if(k==1) printf("%s %c\n",s+1,s[1]);
else if(flag) printf("-1\n");
else{
for(int j=1;j<=anslen;j++) printf("%c",ans[j]);
putchar(' ');
for(int j=1;j<=k;j++) printf("%c",shunxu[j]);
putchar('\n');
}
for(int i=1;i<=k;i++) book[shunxu[i]]=0,cnt[shunxu[i]]=0; k=0; flag=false;
}
return 0;
}
注意:要memset 反例:aaabbb
F - Nearest Beautiful Number
首先因为要最后的数字越小越好,所以大的位越小越好,所以前面的数字尽可能的不变,让后面的数字去变,所以从左到右先找到第一个位置,这个位置之前的数字种类是 k k k种,加上这一位就 k + 1 k+1 k+1种了,所以的话,这一位就是不能要了,这一位就最好变成比原来那个数字大,但是尽可能小的数字,然后后面补 0 0 0就行了,但是还是有问题, 0 0 0也是一种数字,所以可能不能补 0 0 0,要补一种比 0 0 0大,但是尽可能小的数字,然后我们发现,补数字这个操作就相当于我们前面的那个操作,所以这两个操作合到一起就行了.
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
int T,n,k;
int cnt_digit(int x){
int book[11]; memset(book,0,sizeof book);
while(x){book[x%10]=1;x/=10;}
for(int i=0;i<=9;i++) book[10]+=book[i];
return book[10];
}
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld %lld",&n,&k);
while(cnt_digit(n)>k){
int l=1,r=n;
while(cnt_digit(r)>k) l*=10,r/=10;
l/=10;
n=((n/l)+1)*l;
printf("%lld\n",n);
}
}
return 0;
}
总结 :这种题目也不用想时间复杂度,除了一个一个的枚举,就算是暴力也把它写出来也能当对拍,所以要大胆写
关注+点赞 😍