A -火柴棒不等式
题:
给你n根火柴棍,你可以拼出多少个形如“A+B=C”的等式?
等式中的A、B、C是用火柴棍拼出的整数(若该数非零,则最高位不能是0)。
用火柴棍拼数字0-9的拼法如图所示:
注意:
加号与等号各自需要两根火柴棍
如果A≠B 则A+B=C与B+A=C视为不同的等式(A,B,C>=0)
n根火柴棍必须全部用上
输入
一个正整数n, 1<=n<=24
输出
一个整数,能拼成的不同等式的数目
样例输入:
18
样例输出:
9
ps:本来以为是道益智题,没想到是简单暴力emmmmm。
思路:
首先用一个数组去存储十个数字所需要用的火柴棒数。
由题知n最大为24,数字一是用的最少火柴棒数为二,2 * 4 * 3 + 4 = 24 。
所以可知加数和被加数最大不会超过1000,然后双for循环暴力可解。
注意:
0 + 0 = 0 的情况也要考虑,这是可行的 。
#include<iostream>
using namespace std ;
int main()
{
int n ;
cin >> n ;
int a[15] = { 6 , 2 , 5 , 5 , 4 , 5 , 6 , 3 , 7 , 6 } ;
n -= 4 ;
int i , j ,sum = 0 , flag = 0 ;
for( i = 0 ; i < 1000 ; ++i )
{
int m = i , s = 0 ;
if( i == 0 )
{
s = 6 ;
}
while( m )
{
s += a[ m % 10 ] ;
m /= 10 ;
}
for( j = 0 ; j < 1000 ; ++j )
{
int k = j , ss = 0 ;
if( j == 0 )
{
ss = 6 ;
}
while( k )
{
ss += a[ k % 10 ] ;
k /= 10 ;
}
int kk = i + j , sss = 0 ;
if( kk == 0 )
{
sss = 6 ;
}
while( kk )
{
sss += a[ kk % 10 ] ;
kk /= 10 ;
}
if( s + ss + sss == n )
sum++ ;
}
}
cout << sum << endl ;
return 0 ;
}
B-内卷
题
期末有n个同学写论文,第i个人写论文的字数在 [ Li , Ri ] 之间,定义wi为某个同学写的字数。
所以Li≤wi≤Ri
而成绩的得分是非常有趣的,第i个同学gi的论文得分为n-Ki
Ki是比当前这个人的论文字数多的人数
每个人都想写尽可能多的字,因为每位同学都想尽可能得到更高的得分。
所以很容易想到让wi=Ri即可。
但是zks发现了有一个有趣的现象
假设对于每位同学来说,∀i∈[1,n] Li=1000,Ri=10000. 那么在最积极情况下,每位同学都写10000字,即wi=10000.
所以每位同学的得分都为n-0(ki=0,因为没有被任何同学超过)。但是如果每位同学都写1000字,每个人的得分仍然为n-0.
这种现象被称之为内卷
感受到内卷的威力了嘛?!
现在让你尽可能降低1~n位同学所有字数总和的情况下,即min{w1+w2+…+wn} 的同时保证每位同学不能低于在尽自己最大努力时所能得到的分数
你能帮帮zks解决这个问题吗?
输入
第一行有一个正整数n
接下来有n行,第i行有两个正整数Li,Ri
1≤n≤1e5
1≤Li≤Ri≤1e9
输出
输出最小的 w1+w2+…+wn的值
样例输入
3
1 10000
1 10000
1 10000
样例输出
3
提示
样例2:
输入
4
1 2
2 2
2 4
3 4
输出
10
思路:
自定义一个cmp结构体排序,优先利用最大字数从小到大排出名次,其次若名次相同则从大到小排最小字数。
若名次高者所取最小字数比后一名小,则使二者为并列,可达到字数最小学生得到最大努力分数的目的 。
注意:如果排完是并列名次,还需要取并列学生中最小字数的最大值 , 可达到字数最少得分不变的目的 。
#include<iostream>
#include<algorithm>
using namespace std ;
struct nj
{
long a , b ;
}p[100005] ;
bool cmp ( nj x , nj y )
{
if( x.b == y.b )
return x.a > y.a ;
else
return x.b < y.b ;
}
int main()
{
int n , i , j ;
cin >> n ;
p[0].a = p[0].b = 0 ;
p[n+1].a = p[n+1].b = 0 ;
int mm ;
long long sum = 0 ;
for( i = 1 ; i <= n ; ++i )
cin >> p[i].a >> p[i].b ;
sort( p+1 , p+1+n , cmp ) ;
int f = 0 ;
for( i = 1 ; i <= n ; ++i )
{
if( p[i].b != p[i-1].b && p[i].b != p[i+1].b )
{
if( p[i].a <= f )
{
sum += f ;
}
else
{
f = p[i].a ;
sum += f ;
}
}
else
{
if( p[i].b != p[i-1].b )
{
mm = p[i].a ;
if( mm < f && f )
mm = f ;
sum += mm ;
f = mm ;
}
else
{
sum += mm ;
}
}
}
cout << sum << endl ;
return 0 ;
}
C-放在哪儿好呢?
题
zks正在和mqj玩一个数字游戏,游戏规则如下
zks在水平数轴上放置了n个正整数,第i个数为ai
mqj放置了m个正整数,第j个数为bj
现在由瑶姐在水平数轴上任意位置放置一个实数c
如果存在一个ai ( 1≤i≤n) ,对所有bj (1≤j≤m)来说,都满足| c-ai |<| c-bj |时,那么zks将获得一分。
而瑶姐希望zks分数尽可能高,所以实数c的放置位置就至关重要。
如果zks得分为0,输出Impossible
请你帮瑶姐计算zks最高得分为多少
输入
有多组测试数据
第一行为两个正整数n,m (1≤n,m≤1e5)
第二行为n个正整数a1,a2…an
第三行为m个正整数b1,b2…bm
1≤ai,bi≤1e9
输出
输出zks最高的得分,如果为无法得分,则输出Impossible
样例输入
2 2
2 3
1 4
6 5
2 5 3 7 1 7
3 4 3 1 10
1 1
7
7
样例输出
2
3
Impossible
提示
第一个例子:当c=2.5时,zks将得2分
第二个例子: 当c=7时,zks将得3分
第三个例子:无论瑶姐把实数c放在哪 ,zks都无法得到分。
思路:
由于都是正整数,所以定一个区间,0到正无穷,然后把mqj的数字放进这个大区间。0到mqj的数字到正无穷两两相邻之间可以构成很多个小区间。然后再把zks的数字放进去。
去找每个小区间里zks的数字的量,取最大的那个。
注意如果zks的数字跟mqj的数字相同则不计zks的数字,可以直接删掉。
举个例子:
设zks的数字为a,mqj的数字为b。
若是0到b[0]这个区间,假设a[0],a[1],a[2]都小于b[0],可取c为大于0且小于a[0]的数,则a[0],a[1],a[2]比所有b都距离c近。
若是b[0]到b[1]这个区间,假设a[3],a[4],a[5],a[6]都大于b[0]且小于b[1],可取c为区间的中点,则这个区间内的a[3],a[4],a[5],a[6]都满足距离c比所有b近。
显而易见,若a与b重合,则无法满足距离c比所有b近,故删去。
ps:这里还用了unique函数(和sort函数一样位于algorithm头文件),作用是去掉相邻的数的重复的数,本质是将重复的数移动到容器末尾(容器大小不会变),unique函数会返回容器末尾元素的地址,减去初始地址可得size,注意真正去重要先排序再使用这个。
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
using namespace std ;
const int maxn = 1e5 + 5 ;
long a[maxn] , b[maxn] ;
queue< long > h ;
int main()
{
ios::sync_with_stdio(false) ;
cin.tie(0) ;
cout.tie(0) ;
int n , m ;
while( cin >> n >> m )
{
long i , j , c = 0 , num = 0 ;
map< long , int > p ;
for( i = 0 ; i < n ; ++i )
cin >> a[i] ;
for( i = 0 ; i < m ; ++i )
{
cin >> b[i] ;
p[b[i]]++ ;
}
b[m] = 0 , b[m+1] = 0x3f3f3f3f ;
sort( b , b + m + 2 ) ;
sort( a , a + n ) ;
for( i = 0 ; i < n ; ++i )
{
if( !p[a[i]] )
h.push(a[i]) ;
}
long len = unique( b , b + m + 2 ) - b ;
long count = 0 ;
for( i = 1 ; i <= len - 1 ; ++i )
{
count = 0 ;
while( !h.empty() )
{
if( h.front() < b[i] && h.front() > b[i-1] )
{
count++ ;
h.pop() ;
}
else
break ;
}
c = max( c , count ) ;
}
if( !c )
cout << "Impossible" << endl ;
else
cout << c << endl ;
}
return 0 ;
}
D-山不平何以平天下
题
刘俊学长在一个深山老林里冒险,这里到处都是山,第i座山的高度记为hi
当hi-1<hi且hi+1<hi时,刘俊学长将会讨厌这座山,也就是第i座山。因为它需要消耗更多的体力翻越。
然而zks是一位魔法师,他可以使一座山凭空消失,而它旁边的两座山会神奇般的连接起来。
例如有7座山,高度为 1 9 1 9 8 1 0 当对第三座山施加魔法时,剩下的六座山为1 9 9 8 1 0
zks希望在释放魔法后,刘俊学长讨厌的山会尽可能的变少。那么最少会有多少座山令刘俊学长讨厌呢? 魔法只能释放一次。
输入
有多组样例测试
第一行为一个整数n,代表有n座山
第二行包含n个正整数 h1,h2,…,hn,代表山的高度
1≤n≤1e5, 0≤hi≤1e9
输出
在释放魔法后,刘俊最少会讨厌的山的数量
样例输入
6
1 1 4 5 1 4
7
1 9 1 9 8 1 0
10
2 1 4 7 4 8 3 6 4 7
样例输出
1
0
2
思路:
在for循环里讨论三种情况:讨厌山数不变、讨厌山数-1、讨厌山数-2(若两座高度相同的讨厌的山之间只隔一座山)。另外注意特判当讨厌的山数为0时直接输出0即可。
ps:其实是较简单的一道思维题,月赛的时候想复杂了给自己写晕了orz。
#include<iostream>
using namespace std ;
int main()
{
ios::sync_with_stdio(false) ;
cin.tie(0) ;
cout.tie(0) ;
int n , i , j , a[100005] , b[100005] ;
while( cin >> n )
{
int mm = 0x3f3f3f3f , x = 0 ;
for( i = 0 ; i < n ; ++i )
cin >> a[i] ;
for( i = 0 ; i < n ; ++i )
{
if( a[i] > a[i-1] && a[i] > a[i+1] && i-1 >= 0 && i+1 < n )
{
x++ ;
}
}
if( x == 0 )
{
cout << 0 << endl ;
continue ;
}
for( i = 0 ; i < n ; ++i )
{
if( a[i] > a[i-1] && a[i] > a[i+1] && a[i+2] > a[i+1] && a[i+3] < a[i+2] && i-1 >= 0 && i+3 < n && a[i] == a[i+2] )
{
mm = min( mm , x - 2 ) ;
}
else
{
if( a[i] > a[i-1] && a[i] > a[i+1] && i-1 >= 0 && i+1 < n )
{
if( i-2 >= 0 && i+1 < n && a[i-1] > a[i+1] && a[i-2] < a[i-1] || a[i+1] > a[i-1] && a[i+1] > a[i+2] && i-1 <= 0 && i+2 < n )
{
mm = min( mm , x - 0 ) ;
}
else
{
mm = min( mm , x - 1 ) ;
}
}
}
}
cout << mm << endl ;
}
return 0 ;
}
E-排行统计
题:
上周周赛结束了,每个同学都有一个排名,且不存在并列情况
zks在统计协会周赛排名的时候,意外的将一部分同学排名统计错了。
请问,zks将所有同学排名都统计错误的情况有多少种?
输入
第一行输入为一个整数n,代表有n名同学
1≤n≤15
输出
所有同学排名都统计错误的情况数量
样例输入
3
样例输出
2
提示
n=3时,有两种情况:
3 1 2
2 3 1
思路:
错排递推公式:D(n) = ( n - 1 ) * ( D( n - 1 ) + D ( n - 2 ) )
代码一(递归实现):
递归方法比较清晰直接,易于理解,但由于递归本身的问题,效率会较低。
#include<iostream>
using namespace std ;
long long cp( int n )
{
if( n == 1 )
{
return 0 ;
}
if( n == 2 )
{
return 1 ;
}
return ( n - 1 ) * ( cp( n - 1 ) + cp( n - 2 ) ) ;
}
int main()
{
int n ;
cin >> n ;
cout << cp( n ) << endl ;
return 0 ;
}
代码二(数组实现):
#include<iostream>
using namespace std;
int main()
{
int n ;
long long d[21] = { 0 , 0 , 1 };
for( int i = 3 ; i <= 20 ; ++i )
{
d[i] = ( i - 1 ) * ( d[i-1] + d[i-2] ) ;
}
while( cin >> n )
{
cout << d[n] <<endl;
}
return 0;
}
F-买花
题
五一劳动节要到了,zks想送出n朵花给mqj,他打算提前开始买。但是,因为他有强迫症,所有的花要分k天买(k>1,即不能一天全买完),第一天他可以买任意朵花,之后每一天买花的数量为前一天的两倍,(如若第一天买4朵,第二天就要买8朵,以此类推)。 如果现在离劳动节还有15天(k≤15),请你告诉zks,他能不能刚好买到n朵花。
输入
第一行一个正整数T(1<=T<=10^5),表示数据组数。
接下来T行有T个数,每行一个正整数n(1<=n<=10^9),表示预计买花的数量。
输出
每组数据输出一行,共T行。
判断是否能刚好买到n多花,可以则输出"YES",否则输出"N0" (注意看清输出!)
样例输入
2
21
20
样例输出
YES
N0
提示
数据量大,请用scanf, printf
思路:
设第一天买的花为x朵,第二天买2x朵,一共有3x朵,第三天买4x朵,一共有7x朵,以此类推到十五天就够了,用个数组d存起来,如果要买的花数%d为0,则可以买到。
( yh大佬yyds!)
注意:这题数据有点ex,加速后的cin还是会被卡QAQ,还是乖乖用scanf叭(弱弱吐槽一句scanf写起来真的好麻烦唉)
ps:这题,唉,比赛结束后yh大佬教我的时候我才发现我题目都看错了,我以为是后一天拥有的花是前一天拥有的花的两倍QAQ,今天也被自己蠢哭了呢/。
#include<iostream>
#include<cstdio>
using namespace std ;
int main()
{
int t ;
scanf( "%d" , &t ) ;
int a[20] , d ;
a[0] = d = 1 ;
for( int i = 1 ; i < 15 ; ++i )
{
d = 2 * d ;
a[i] = a[i-1] + d ;
}
while( t-- )
{
long n ;
scanf( "%ld" , &n ) ;
int flag = 0 ;
for( int i = 1 ; i < 15 ; ++i )
{
if( n % a[i] == 0 )
{
flag = 1 ;
break ;
}
}
if( flag )
printf( "YES\n" ) ;
else
printf( "N0\n" ) ;
}
return 0 ;
}
G-Race
题:
zks和mqj喜欢赛跑。mqj虽然跑得比较快,但是他在任一时刻领先T米或以上,他就会停下来休息S秒。
现在告诉你mqj的速度V1(m/s)和zks的速度V2(m/s),T(米)和S(秒)的值,以及赛道的长度L(米),请你预测zks和mqj谁会赢。
输入
输入一行,五个正整数V1,V2,T,S,L(L是V1,V2的公倍数)
(1 ≤ V1, V2 ≤ 100,1 ≤ T ≤ 300,1 ≤ S ≤ 10,1 ≤ L ≤ 10000)
输出
输出一行,比赛的结果和获胜者或两人同时到达终点的时间(秒),中间用空格分开
字符串"Mqj"、"Zks"和"Tie"分别表示mqj获胜、zks获胜和他们同时到达终点
样例输入
2 1 1 2 8
样例输出
Tie 8
注意:顺序问题QAQ,mqj是不能连续休息的,如果已经休息了一次,但是zks距离mqj还是大于t米,则mqj要跑一秒再休息。
ps:wa了将近二十次,我以为,只要他们之间的距离大于t米就会一直休息,哭辽
#include<iostream>
using namespace std;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
long v1, v2, t, s, p, i, tt = 0;
cin >> v1 >> v2 >> t >> s >> p;
long s1 = 0, s2 = 0;
while (1)
{
if (s1 == p || s2 == p)
break;
s1 += v1;
s2 += v2;
tt++;
if (s1 - s2 >= t&&s1<p&&s2<p)
{
int k;
for (k = 1; k <= s; ++k)
{
s2 += v2;
if (s2 >= p)
break;
}
if (k > s)
tt += s;
else
tt += k;
}
}
if (s1 == s2)
{
cout << "Tie " << tt << endl;
}
else
{
if (s1 > s2)
{
cout << "Mqj " << tt << endl;
}
else
{
cout << "Zks " << tt << endl;
}
}
return 0;
}
H-最小互质数
题
我们定义两个数的互质数当且仅当gcd(a, b) = 1。
现在L手里有n个数,分别为a1,a2,a3 ……an-1,an 。
问,没有在这n个数中出现过并且与这n个数都互质的最小的数是多少。
LL觉得这个问题太简单了,于是她把这个问题交给你来解决。
输入
第一行一个数n (1 ≤ n, ai ≤ 10^5)
接下来n行,每行一个数,分别代表a1,a2,a3 ……an-1,an 。
输出
输出一行代表答案
样例输入
5
1
2
3
4
5
样例输出
7
提示
没有在这n个数中出现的数有:6,7,……
6与2, 3, 4不互质,最小的与这n个数互质的数就是7了。
思路:
显然如果给的数没有1就输出1,有1的话,那答案肯定是在质数里面选啦!
这里可以先用一个欧拉筛素数打表,注意要在里面加一个标记质因数的东西!
///然后oj上一直ac88%,牛客上数据水了倒是ac了。///
如果是给的数里有该质数的倍数那么这个质数也要pass掉!
///昨晚wa了好多次,本来有想到这个的,但是一看数据1e5就有点不确定了,感觉肯定会超时,打完就删掉了QAQ,后悔没有交一发了。///
事实上,如果真的给出1e5个数的话,那么能选的质数也会更少,复杂度差不多是O(n)。
ps:超级感谢mo哥超级耐心的帮我找bug!!!
#include<iostream>
#include<map>
using namespace std ;
const int maxn = 1e6 + 5 ;
int a[maxn] , prime[maxn] , countnum = 0 ;
bool N[maxn] = { false } ;
map < int , int > p ;
void isprime( )
{
int i , j ;
for( i = 2 ; i <= maxn ; ++i )
{
if( !N[i] )
prime[countnum++] = i ;
for( j = 0 ; j < countnum ; ++j )
{
if( i * prime[j] > maxn )
break ;
N[ i * prime[j] ] = true ;
if( p[ i * prime[j] ] )
{
p[i]++ ;
p[prime[j]]++ ;
}
if( i % prime[j] == 0 )
break ;
}
}
}
int main()
{
int i , j , n , flag = 0 ;
cin >> n ;
for( i = 1 ; i <= n ; ++i )
{
cin >> a[i] ;
p[a[i]]++ ;
}
if( !p[1] )
{
cout << 1 << endl ;
return 0 ;
}
isprime() ;
for( i = 0 ; i < countnum ; ++i )
{
if( !p[ prime[i] ] )
{
for( j = 1 ; j <= n ; ++j )
{
if( a[j] % prime[i] == 0 )
{
break ;
}
}
if( j > n )
break ;
}
}
cout << prime[i] << endl ;
return 0 ;
}
J-上进的LZ
题
LZ个上进的人,他的人生没有下坡路,他也讨厌带有”下坡路“的东西。 所以,对于LZ来说,只有非降序的数组才是nice的(如:1,2,2,3,4,5,5);若数组元素个数为1,也满足非降序,也是nice的。 现在有一个长度为n的数组,LZ想知道它的子数组中有多少个数组是nice的。
你能帮帮他吗? 对于子数组的定义,如果可以通过从开头和从结束分别删除若干个(可以为零或全部,前后删除个数不必相同)元素来从数组b获得数组a,则称数组a是数组b的子数组。(子数组包含原数组,但不包含空串)
输入
第一行输入一个整数n(1≤n≤10^5),表示数组的长度。
第二行包含n个空格分隔的整数a1,a2,.,an(0≤ai≤10^9),为数组的元素
输出
输出的给定数组的子数组是nice数组的个数。
样例输入
5
1 2 3 4 5
样例输出
15
思路:
首先遍历,找到比前一个元素小的元素,并标记,存进数组,这个数组相邻的两个数组成一个非降序区间,利用等差数列的和公式去求出每一个区间的子数组个数,然后加起来即可。
ps:这个题月赛的时候敲的真的好坎坷QAQ,两次看错题我也是醉了,后来终于看对了题意,然后用的这个思路敲了一发wa了心态就崩了,应该是当时太浮躁了细节问题没搞好orz
#include<iostream>
#include<algorithm>
using namespace std ;
const int maxn = 1e5 + 5 ;
long a[maxn] , b[maxn] ;
int main()
{
ios::sync_with_stdio(false) ;
cin.tie(0) ;
cout.tie(0) ;
int n , num = 1 , i ;
cin >> n ;
for( i = 1 ; i <= n ; ++i )
cin >> a[i] ;
for( i = 2 ; i <= n ; ++i )
{
if( a[i] < a[i-1] )
{
b[num++] = i ;
}
}
if( !num )
{
cout << n+ n * (n-1) / 2 << endl ;
return 0 ;
}
b[num++] = n + 1 ;
b[0] = 1 ;
long long c = 0 , s = 0 ;
while( 1 )
{
long long nn = 0 ;
for( i = b[c] ; i < b[c+1] ; ++i )
{
nn++ ;
}
if( nn == 1 )
s += 1 ;
else
s += nn + nn * ( nn - 1 ) / 2 ;
if( c == num )
break ;
c++ ;
}
cout << s << endl ;
return 0 ;
}
K-填数
题
小莫拿到了一个数,数的大小不超过五位数,且不含前导0。并且每一位的数字都只出现一次。然后zks将其中的几位数字标记为’?’
而’?'可以表示从1~9之间的任何数,但不能和现有的数重复出现。小莫需要统计出有多少种数可以正好被m整除,并打印出来
如果小莫不会这个问题,他会被zks叫成老莫的,请你快帮帮他!
输入
第一行输入两个正整数n,m n代表字符串的长度,m代表除数 1≤m≤1e9
第二行输入一个字符串,其中至少包含一个’?’
字符串长度不超过5位
输出
从小到大输出符合的数,每个数独占一行
如果不存在,则输出NO
样例输入
4 5
?23?
样例输出
1235
4235
6235
7235
8235
9235
思路一:
取最小值和最大值遍历。
ps:这题方法很简单,不过我太菜了,写出来好多bug,第一遍好不容易打出来结果输出都没有,心态崩了,休息了一下,纠正了半小时bug,然后突然就ac了。
#include<iostream>
#include<memory.h>
#include<string>
#include<map>
using namespace std ;
#define bug(a) cout << "* " << a << endl ;
int main()
{
int n , m , i , ff = 0 ;
int b[10] = { 0 , 1 , 10 , 100 , 1000 , 10000 , 100000 } ;
cin >> n >> m ;
string a ;
cin >> a ;
int len = a.length() , s = 0 , c[10] = { 0 } ;
map< int , int > p , q ;
map< int , int >:: iterator it ;
for( i = 0 ; i < len ; ++i )
{
if( a[i] != '?' )
{
p[ a[i] - '0']++ ;
s += (a[i]-'0') * b[len - i] ;
}
}
for( i = b[len] ; i < b[len + 1] ; ++i )
{
if( i % m != 0 || i % 10 == 0 )
continue ;
int o = i , flag = 0 , num = 0 ;
q.clear() ;
while( o )
{
c[num++] = o % 10 ;
if( o%10 == 0 )
{
flag = 1 ;
break ;
}
if( len - num >= 0 && a[len - num] != '?' && c[num-1] != a[len-num] - '0' )
{
flag = 1 ;
break ;
}
else
{
if( len - num >= 0 && a[len - num] == '?' && ( p[c[num-1]] || q[c[num-1]] ) )
{
flag = 1 ;
break ;
}
}
q[c[num-1]]++ ;
o /= 10 ;
}
if( flag )
continue ;
cout << i << endl ;
ff = 1 ;
}
if( !ff )
cout << "NO" << endl ;
return 0 ;
}
思路二:
用暴力搜索dfs!
ps:我还是不太会用dfs,这个是看了克总的代码,感觉看懂了,然后自己又打了一遍,克总yyds!!!
#include<iostream>
#include<string>
#include<map>
using namespace std ;
map< int , int > p ;
int sum = 0 , len , n , m ;
string s ;
void dfs( int now , int num )
{
if( now == len )
{
if( num % m == 0 )
{
cout << num << endl ;
sum++ ;
}
return ;
}
else
{
if( s[now] == '?' )
{
for( int i = 1 ; i <= 9 ; ++i )
{
if( !p[i] )
{
p[i]++ ;
dfs( now + 1 , num * 10 + i ) ;
p[i] = 0 ;
}
}
}
else
{
dfs( now + 1 , num * 10 + s[now] - '0' ) ;
}
}
}
int main()
{
ios::sync_with_stdio(false) ;
cin.tie(0) ;
cout.tie(0) ;
cin >> n >> m >> s ;
len = s.length() ;
for( int i = 0 ; i < len ; ++i )
{
if( s[i] != '?' )
{
p[ s[i] - '0' ]++ ;
}
}
dfs( 0 , 0 ) ;
if( !sum )
cout << "NO" << endl ;
return 0 ;
}