题目链接(洛谷)

题目大意

给定两个数 \(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. 如何在windows上升级Powershell到5.1版本?

    前言 此篇我们说的是Powershell5.1低版本到5.1的升级,对于Powershell6(及以上版本)可以跨平台独立安装,在windows上可与之前的版本并存. 首先要整清楚Powershell ...

  2. k8s二进制部署 - master节点安装

    下载kubernetes服务端 [root@hdss7-21 ~]# cd /opt/src [root@hdss7-21 src]# wget https://dl.k8s.io/v1.15.2/k ...

  3. Django用户注册、登录

    一.用户注册 1 ''' 2 注册的表单模型 3 forms.py 的例子 4 ''' 5 6 from django import forms #表单功能 7 from django.contrib ...

  4. Shell 元字符 & 变量

    Shell 介绍 ## 什么是程序 程序就是一组数据和代码逻辑集合的文件 ## 什么是进程 进程是程序的运行过程,也可以说是操作系统干活的过程,因为是操作系统负责控制硬件来运行应用程序 ps:进程与进 ...

  5. python了解未知函数的方法

    ?func 如图:

  6. 谈一谈phar 反序列化

    前言 来自Secarma的安全研究员Sam Thomas发现了一种新的漏洞利用方式,可以在不使用php函数unserialize()的前提下,引起严重的php对象注入漏洞.这个新的攻击方式被他公开在了 ...

  7. 写给程序员的机器学习入门 (十一) - 对象识别 YOLO - 识别人脸位置与是否戴口罩

    这篇将会介绍目前最流行的对象识别模型 YOLO,YOLO 的特征是快,识别速度非常快

  8. Fetch & POST

    Fetch & POST fetch( `http://10.1.5.202/deploy/http/send/viewtree`, { method: "POST", m ...

  9. Taro 框架实现原理

    Taro 框架实现原理 小程序 H5 RN Web 多端框架 Taro 1.x & Taro 2.x 编译型架构, 语法编译转换 Taro 1/2 属于编译型架构,主要通过对类 React 代 ...

  10. 1月加密货币交易所访问量破3亿!NGK生态星空计划、NGK生态所带来双重利好!

    据最新数据显示,2021年一月份,加密货币交易所网站的访问量急剧上升.约有3.44亿访问者涌入了加密货币交易所,超过2020年12月的1.96亿访问者总数,创2018年1月以来新高. 加密货币交易所网 ...