/*
同余的定义:如果m|(a-b)则a同余b即a与b对m取余后的余数相同
定理:
【1】如果a1同余b1(mod m) a2同余b2(mod m)则a1*a2同余b1*b2(mod m)
【2】如果a1同余b1(mod m) a2同余b2(mod m)则a1+a2同余b1+b2(mod m)
证明:
1)a1=b1+k1*m a2=b2+k2*m
a1*a2=b1*b2+(k1*b2+k2*b1+k1*k2*m)m
所以a1*a2同余b1*b2(mod m)
2)
a1=b1+k1*m a2=b2+k2*m
a1+a2=b1+b2+(k1+k2)m
所以a1+a2同余b1+b2(mod m)
如果a和b同余则成上同一个数后一定还是同余的
证明:假设数乘上一个数x
a同余b(mod m) x同余x(mod m)
运用上述定理得ax同余bx(mod m)
举个例子:
N=11 C=10
10
0 1 2 3 4 5 6 7 8 9 10
123同余2(mod m)当在后面加上一个数后相当于先乘上10 两个数还同余 在加上同一个数后还同余 即余数相同那么就没必要
搜123了
所以我们标记一下余数就可以了,因为余数最多有5000个
*/
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
int num[20],N,C,flag[5005];//flag数组标记r数是否出现过如果余数已经出现过就没必要在进行搜索了,num的0,1判断是否有这个数,也减少了排序
struct node
{
int nu[505];
int len;//数的长度
};
void Print(node q)//数的输出
{
int i;
for(i=0; i<q.len; i++)
{
if(q.nu[i]<10)
printf("%d",q.nu[i]);
else
printf("%c",q.nu[i]+'A'-10);
}
puts("");
}
int Mod(node q)//大数取模
{
int i,temp=0;
for(i=0; i<q.len; i++)
{
temp=(temp*C+q.nu[i])%N;
}
return temp;
}
int bfs()
{
int i;
queue<node> v;
while(!v.empty())
v.pop();
node l;
for(i=1; i<16; i++)//这一层for循环是把每个元素都放到队列里头,队列中的点的长度都为1
{
if(!num[i])//当num[i]为0时说明i这个数不存在
continue;
l.len=1;//把每个数的长度都初始化为1
l.nu[0]=i;
int r=Mod(l);
if(r==0)
{
Print(l);
return 1;
}
else
{
flag[r]=1;
v.push(l);
}
}
//这时队列里头的元素的长度都为1
while(!v.empty())
{
l=v.front();
v.pop();
for(i=0; i<16; i++)
{
if(num[i]==0)
continue;
l.nu[l.len]=i;
l.len++;//这个地方自加1是在l这个点后依次加上每个数后在判断
if(l.len>499)
continue;
int r=Mod(l);
if(r==0&&l.len<500)
{
Print(l);
return 1;
}
else
{
if(flag[r]==0&&l.len<499)
{
flag[r]=1;
v.push(l);
}
}
l.len--;//这个地方减1还是保持l这个点的长度变为原来的长度(这样就相当于用这个点向四周扩散)
}
}
return 0;
}
int main()
{
int t,i,j,m;
char dd;
scanf("%d",&t);
while(t--)
{
memset(num,0,sizeof(num));
memset(flag,0,sizeof(flag));
scanf("%d%d%d",&N,&C,&m);
for(i=0; i<m; i++)
{
getchar();
scanf("%c",&dd);
if(dd>='A')
num[dd-'A'+10]=1;
else
num[dd-='0']=1;
}
if(N==0)
{
if(num[0])
printf("0\n");
else
printf("give me the bomb please\n");
continue;
}
int aa=bfs();
if(!aa)
printf("give me the bomb please\n");
}
return 0;
}
同余的定义:如果m|(a-b)则a同余b即a与b对m取余后的余数相同
定理:
【1】如果a1同余b1(mod m) a2同余b2(mod m)则a1*a2同余b1*b2(mod m)
【2】如果a1同余b1(mod m) a2同余b2(mod m)则a1+a2同余b1+b2(mod m)
证明:
1)a1=b1+k1*m a2=b2+k2*m
a1*a2=b1*b2+(k1*b2+k2*b1+k1*k2*m)m
所以a1*a2同余b1*b2(mod m)
2)
a1=b1+k1*m a2=b2+k2*m
a1+a2=b1+b2+(k1+k2)m
所以a1+a2同余b1+b2(mod m)
如果a和b同余则成上同一个数后一定还是同余的
证明:假设数乘上一个数x
a同余b(mod m) x同余x(mod m)
运用上述定理得ax同余bx(mod m)
举个例子:
N=11 C=10
10
0 1 2 3 4 5 6 7 8 9 10
123同余2(mod m)当在后面加上一个数后相当于先乘上10 两个数还同余 在加上同一个数后还同余 即余数相同那么就没必要
搜123了
所以我们标记一下余数就可以了,因为余数最多有5000个
*/
#include<stdio.h>
#include<string.h>
#include<queue>
using namespace std;
int num[20],N,C,flag[5005];//flag数组标记r数是否出现过如果余数已经出现过就没必要在进行搜索了,num的0,1判断是否有这个数,也减少了排序
struct node
{
int nu[505];
int len;//数的长度
};
void Print(node q)//数的输出
{
int i;
for(i=0; i<q.len; i++)
{
if(q.nu[i]<10)
printf("%d",q.nu[i]);
else
printf("%c",q.nu[i]+'A'-10);
}
puts("");
}
int Mod(node q)//大数取模
{
int i,temp=0;
for(i=0; i<q.len; i++)
{
temp=(temp*C+q.nu[i])%N;
}
return temp;
}
int bfs()
{
int i;
queue<node> v;
while(!v.empty())
v.pop();
node l;
for(i=1; i<16; i++)//这一层for循环是把每个元素都放到队列里头,队列中的点的长度都为1
{
if(!num[i])//当num[i]为0时说明i这个数不存在
continue;
l.len=1;//把每个数的长度都初始化为1
l.nu[0]=i;
int r=Mod(l);
if(r==0)
{
Print(l);
return 1;
}
else
{
flag[r]=1;
v.push(l);
}
}
//这时队列里头的元素的长度都为1
while(!v.empty())
{
l=v.front();
v.pop();
for(i=0; i<16; i++)
{
if(num[i]==0)
continue;
l.nu[l.len]=i;
l.len++;//这个地方自加1是在l这个点后依次加上每个数后在判断
if(l.len>499)
continue;
int r=Mod(l);
if(r==0&&l.len<500)
{
Print(l);
return 1;
}
else
{
if(flag[r]==0&&l.len<499)
{
flag[r]=1;
v.push(l);
}
}
l.len--;//这个地方减1还是保持l这个点的长度变为原来的长度(这样就相当于用这个点向四周扩散)
}
}
return 0;
}
int main()
{
int t,i,j,m;
char dd;
scanf("%d",&t);
while(t--)
{
memset(num,0,sizeof(num));
memset(flag,0,sizeof(flag));
scanf("%d%d%d",&N,&C,&m);
for(i=0; i<m; i++)
{
getchar();
scanf("%c",&dd);
if(dd>='A')
num[dd-'A'+10]=1;
else
num[dd-='0']=1;
}
if(N==0)
{
if(num[0])
printf("0\n");
else
printf("give me the bomb please\n");
continue;
}
int aa=bfs();
if(!aa)
printf("give me the bomb please\n");
}
return 0;
}