題目大意
給定兩個數 \(u\) , \(v\) 。有三種操作:
- \(u=u+1(mod\) \(p)\) 。
- \(u=u+p−1(mod\) \(p)\) 。
- \(u=u^{p−2}(mod\) \(p)\) 。
思路
BFS
狀態太多導致佇列裝不下。
迭代加深
\(TLE\) ,浪費了太多時間在每一層上,沒有記憶化且狀態很多。
IDA*
不行,無法得出樂觀估價函式。
雙向BFS
這樣會將步數很為兩半,狀態相較於普通的 \(BFS\) 會少很多。
先來看操作一和操作二,他們的關係是可以互逆的。一個對於原數 \(+1\) ,另一個對於原數 \(-1\) 。
操作三和操作三是互逆的,由費馬小定理可知:若 \(p\) 為質數,則 \(a^{p-1}≡1(mod\) \(p)\)。
可得出:\((u^{p-2})^{p-2}≡u^{(p-2)(p-2)}≡u^{(p-1)(p-3)+1}≡(u^{p-1})^{p-3}u≡u(mod\) \(p)\)
那麼就分別由開始狀態與結束狀態來向中間推進。
Code
#include <map>
#include <queue>
#include <cstdio>
#include <iostream>
using namespace std;
struct Status {//佇列中儲存的狀態
int step, number, flag;//分別是:步數,當前狀態的數,正向或者反向
Status() {}
Status(int S, int N, int F) {
step = S;
number = N;
flag = F;
}
};
const int MAXN = 1e6 + 5;
queue<Status> q;
map<int, int> real;
bool vis[2][MAXN];//是否訪問過
int dis[2][MAXN];//步數
pair<int, int> pre[2][MAXN];//first記錄前一個數的雜湊值,second記錄操作的序號
int u, v, p;
int tot;
int Quick_Pow(int fpx, int fpy) {//快速冪
long long res = 1;
long long x = fpx;
long long y = fpy;
while(y) {
if(y & 1)
res = (res * x) % p;
x = (x * x) % p;
y >>= 1;
}
int ans = res;
return ans;
}
int Get_Hash(int x) {//map對映假雜湊
map<int, int>::iterator it = real.find(x);
if(it != real.end())
return (*it).second;
real[x] = ++tot;
return tot;
}
void Print(int x, int y) {//輸出路徑:記錄每個字首
if(y == -1)
return;
if(!x) {//前半部分倒著輸出
if(pre[x][y].first != -1) {
Print(x, pre[x][y].first);
printf("%d ", pre[x][y].second);
}
}
else {//後半部分正著輸出
if(pre[x][y].first != -1) {
printf("%d ", pre[x][y].second);
Print(x, pre[x][y].first);
}
}
}
void DoubleBfs() {
int tmp;
q.push(Status(0, u, 0));//初始化兩個狀態
q.push(Status(0, v, 1));
tmp = Get_Hash(u);
vis[0][tmp] = 1;
pre[0][tmp].first = -1;
tmp = Get_Hash(v);
vis[1][tmp] = 1;
pre[1][tmp].first = -1;
while(!q.empty()) {
Status now = q.front();
q.pop();
int skt = Get_Hash(now.number);
if(vis[!now.flag][skt]) {//碰頭了輸出並跳出
printf("%d\n", dis[!now.flag][skt] + dis[now.flag][skt]);
if(pre[0][skt].first != -1) {
Print(0, pre[0][skt].first);
printf("%d ", pre[0][skt].second);
}
if(pre[1][skt].first != -1) {
printf("%d ", pre[1][skt].second);
Print(1, pre[1][skt].first);
}
return;
}
Status next = now;
next.step++;
next.number = (next.number + 1) % p;
tmp = Get_Hash(next.number);
if(!vis[now.flag][tmp]) {//沒有被訪問則訪問
vis[now.flag][tmp] = 1;
dis[now.flag][tmp] = next.step;
pre[now.flag][tmp].first = skt;
if(now.flag)
pre[now.flag][tmp].second = 2;//若是倒著的,則該操作為1
else
pre[now.flag][tmp].second = 1;//若是正著的,則該操作為2
q.push(next);
}
next = now;
next.step++;
next.number = (next.number + p - 1) % p;
tmp = Get_Hash(next.number);
if(!vis[now.flag][tmp]) {//同上
vis[now.flag][tmp] = 1;
dis[now.flag][tmp] = next.step;
pre[now.flag][tmp].first = skt;
if(now.flag)
pre[now.flag][tmp].second = 1;
else
pre[now.flag][tmp].second = 2;
q.push(next);
}
next = now;
next.step++;
next.number = Quick_Pow(next.number, p - 2) % p;
tmp = Get_Hash(next.number);
if(!vis[now.flag][tmp]) {//同上
vis[now.flag][tmp] = 1;
dis[now.flag][tmp] = next.step;
pre[now.flag][tmp].first = skt;
pre[now.flag][tmp].second = 3;//自己的逆操作就是自己
q.push(next);
}
}
}
int main() {
scanf("%d %d %d", &u, &v, &p);
DoubleBfs();
return 0;
}