题目链接(洛谷)

题目大意

给定两个数 \(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

#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;
}

CF995E Number Clicker (双向BFS)的更多相关文章

  1. CF995E Number Clicker 解题报告

    CF995E Number Clicker 题目描述 Allen is playing Number Clicker on his phone. He starts with an integer u ...

  2. CF995E Number Clicker

    题目分析 首先,我们必须明白,操作都是互逆的,\(1,2\)之间是可以互相转化的,这是不需证明的,对于操作\(3\),实际上,是求当前数的逆元,我们知道,逆元就是求当前数在模另一个数下的倒数,那么,逆 ...

  3. Number Clicker CodeForces - 995E(双向bfs)

    双向bfs  注意数很大  用map来存 然后各种难受....

  4. CodeForces - 995E Number Clicker (双向BFS)

    题意:给出u,v,p,对u可以进行三种变化: 1.u=(u+1)%p ; 2.u = (u+p-1)%p;  3.u = 模p下的逆元.问通过几步可以使u变成v,并且给出每一步的操作. 分析:朴素的b ...

  5. HDU 3085 Nightmare Ⅱ (双向BFS)

    Nightmare Ⅱ Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  6. Hdu1401-Solitaire(双向bfs)

    Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered ...

  7. UVA1601-The Morning after Halloween(双向BFS)

    Problem UVA1601-The Morning after Halloween Accept: 289 Submit: 3136 Time Limit: 12000 mSec  Problem ...

  8. Eight (HDU - 1043|POJ - 1077)(A* | 双向bfs+康拓展开)

    The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've see ...

  9. HDU3085(双向BFS+曼哈顿距离)题解

    Nightmare Ⅱ Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

随机推荐

  1. 踏上Revit二次开发之路 1 准备工作

    1 准备工作 工欲善其事,必先利其器.在正式开始之前,我觉得有必要先盘点一下需要准备些什么. 1.1 硬件设备 PC机一台(谢绝Apple). 配置不能太低,至少要i3以上的cpu.4g以上的内存和支 ...

  2. [Golang]-3 函数、多返回值、变参、闭包、递归

    // test01 project main.go package main import ( "fmt" ) // 单返回值的函数 func plus(a int, b int) ...

  3. 在4.0框架下使用Sqlite数据库

    在4.0框架下使用Sqlite数据库出现"混合模式程序集是针对"v2.0.50727"版的运行时生成的,在没有配置其他信息的情况下,无法在 4.0 运行时中加载该程序集. ...

  4. spring-cloud-eureka-client-consumer

    服务注册中心eureka-server已经搭好,并且SPRING-CLOUD-NETFLIX-EUREKA-CLIENT-APPLICATION提供一个hello服务 编写一个eureka-clien ...

  5. Gym 101170F Free Weights(二分)题解

    题意:给出两行,每一行都有n个数组,一共有2 * n个,大小为1~n,每个有两个.现在可以进行操作:拿出一个物品i,然后放到一个空格,花费i.可以任意平移物品,平移没有花费.每一行空间无限.要求你把一 ...

  6. 网络流学习-Ford-Fulkerson

    首先我们先解决最大流问题 什么是最大流问题呢 根据我的理解,有一个源点s,汇点t,s可以通过一个网络(雾)流向汇点t 但是每一条边都有他的最大传输容量限制,那么我们的任务是,如何分配流量使得..从s流 ...

  7. linux 基础正则表达式练习

    感谢鸟哥!!! 如果Linux能够直接连网络,使用以下命令还获取文件吧 wget http://linux.vbird.org/linux_basic/0330regularex/regular_ex ...

  8. Windows中VS code无法查看C++ STL容器的值 - 解决方法

    Windows中VS code debug时无法查看C++ STL容器内容 首先,你很可能用的是x64版本的Windows. 我发现一个有效的解决方法,但在x64版本的Windows上安装MinGW时 ...

  9. URL parser All In One

    URL parser All In One const url = new URL(`https://admin:1234567890@cdn.xgqfrms.xyz:8080/logo/icon.p ...

  10. how to enable vue cli auto open the localhost url

    how to enable vue cli auto open the localhost URL bad you must click the link by manually, waste of ...