同餘的定義:如果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;
}