1. 程式人生 > >CF995E Number Clicker (雙向BFS)

CF995E Number Clicker (雙向BFS)

[題目連結(洛谷)](https://www.luogu.com.cn/problem/CF995E) # 題目大意 給定兩個數 $u$ , $v$ 。有三種操作: 1. $u=u+1(mod$ $p)$ 。 2. $u=u+p−1(mod$ $p)$ 。 3. $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 ```cpp #include #include #include #include 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 q; map real; bool vis[2][MAXN];//是否訪問過 int dis[2][MAXN];//步數 pair 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::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