CF995E Number Clicker (双向BFS)
题目大意
给定两个数 \(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;
}
CF995E Number Clicker (双向BFS)的更多相关文章
- CF995E Number Clicker 解题报告
		CF995E Number Clicker 题目描述 Allen is playing Number Clicker on his phone. He starts with an integer u ... 
- CF995E Number Clicker
		题目分析 首先,我们必须明白,操作都是互逆的,\(1,2\)之间是可以互相转化的,这是不需证明的,对于操作\(3\),实际上,是求当前数的逆元,我们知道,逆元就是求当前数在模另一个数下的倒数,那么,逆 ... 
- Number Clicker CodeForces - 995E(双向bfs)
		双向bfs 注意数很大 用map来存 然后各种难受.... 
- 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 ... 
- HDU 3085 Nightmare Ⅱ (双向BFS)
		Nightmare Ⅱ Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ... 
- Hdu1401-Solitaire(双向bfs)
		Solitaire is a game played on a chessboard 8x8. The rows and columns of the chessboard are numbered ... 
- UVA1601-The Morning after Halloween(双向BFS)
		Problem UVA1601-The Morning after Halloween Accept: 289 Submit: 3136 Time Limit: 12000 mSec Problem ... 
- 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 ... 
- HDU3085(双向BFS+曼哈顿距离)题解
		Nightmare Ⅱ Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ... 
随机推荐
- dict与set -- Python
			dict(字典):用空间换取时间,占据空间大,但查询速度快,键值对(key:value),key唯一 d = {'Michael': 95, 'Bob': 75, 'Tracy': 85} 由于一个k ... 
- 大规模数据爬取 -- Python
			Python书写爬虫,目的是爬取所有的个人商家商品信息及详情,并进行数据归类分析 整个工作流程图: 第一步:采用自动化的方式从前台页面获取所有的频道 from bs4 import Beautiful ... 
- 【非原创】LightOJ-1274 Beating the Dataset【期望dp】
			学习博客:戳这里 
- 初学算法之最基础的stl队列
			简记为先进先出(first in first out) 它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作. 实用: #include <queue>//头 ... 
- Leetcode(14)-最长公共前缀
			编写一个函数来查找字符串数组中的最长公共前缀. 如果不存在公共前缀,返回空字符串 "". 示例 1: 输入: ["flower","flow" ... 
- Ubuntu下跑通py-faster-rcnn、详解demo运作流程
			在不同的服务器不同的机器上做过很多次实验,分别遇到各种不一样的错误并且跑通Py-Faster-RCNN,因此,在这里做一个流程的汇总: 一.下载文件: 首先,文件的下载可以有两种途径: 1.需要在官网 ... 
- mysql 查询,天,周,月等写法
			1.查询当天的数据 select * from 表名 where TO_DAYS(时间字段)=TO_DAYS(NOW()); 2.查询当周的数据 select * from 表名 where YEAR ... 
- Asp.Net Core Grpc 入门实践
			Grpc简介 gRPC 是一种与语言无关的高性能远程过程调用 (RPC) 框架. 在 gRPC 中,客户端应用程序可以直接调用不同计算机上的服务器应用程序上的方法,就像它是本地对象一样,从而更轻松地创 ... 
- React Query & SWR
			React Query & SWR HTTP request all in one solution React Query Hooks for fetching, caching and u ... 
- React Hooks: useLayoutEffect All In One
			React Hooks: useLayoutEffect All In One useLayoutEffect https://reactjs.org/docs/hooks-reference.htm ... 
