CF995E Number Clicker (双向BFS)

原博客地址

题目链接(洛谷)

题目大意

给定两个数 u u u v v v 。有三种操作:

  1. u = u + 1 ( m o d u=u+1(mod u=u+1(mod p ) p) p)
  2. u = u + p − 1 ( m o d u=u+p−1(mod u=u+p1(mod p ) p) p)
  3. u = u p − 2 ( m o d u=u^{p−2}(mod u=up2(mod p ) p) p)

思路

BFS

状态太多导致队列装不下。

迭代加深

T L E TLE TLE ,浪费了太多时间在每一层上,没有记忆化且状态很多。

IDA*

不行,无法得出乐观股价函数。

双向BFS

这样会将步数很为两半,状态相较于普通的 B F S BFS BFS 会少很多。

先来看操作一和操作二,他们的关系是可以互逆的。一个对于原数 + 1 +1 +1 ,另一个对于原数 − 1 -1 1

操作三和操作三是互逆的,由费马小定理可知:若 p p p 为质数,则 a p − 1 ≡ 1 ( m o d a^{p-1}≡1(mod ap11(mod p ) p) p)

可得出: ( u p − 2 ) p − 2 ≡ u ( p − 2 ) ( p − 2 ) ≡ u ( p − 1 ) ( p − 3 ) + 1 ≡ ( u p − 1 ) p − 3 u ≡ u ( m o d (u^{p-2})^{p-2}≡u^{(p-2)(p-2)}≡u^{(p-1)(p-3)+1}≡(u^{p-1})^{p-3}u≡u(mod (up2)p2u(p2)(p2)u(p1)(p3)+1(up1)p3uu(mod p ) p) 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
from PyQt5.QtCore import QTimer from PyQt5.QtGui import QCursor from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton class MouseClicker(QWidget): def init(self): super().init() # 设置窗口标题 self.setWindowTitle("鼠标连点器") # 创建UI界面 self.label_count = QLabel("点击次数:") self.edit_count = QLineEdit() self.label_interval = QLabel("间隔时间(ms):") self.edit_interval = QLineEdit() self.btn_start = QPushButton("开始") # 设置布局 layout_count = QHBoxLayout() layout_count.addWidget(self.label_count) layout_count.addWidget(self.edit_count) layout_interval = QHBoxLayout() layout_interval.addWidget(self.label_interval) layout_interval.addWidget(self.edit_interval) layout_main = QVBoxLayout() layout_main.addLayout(layout_count) layout_main.addLayout(layout_interval) layout_main.addWidget(self.btn_start) self.setLayout(layout_main) # 连接按钮的点击事件 self.btn_start.clicked.connect(self.start_clicking) # 创建计时器 self.timer = QTimer() self.timer.timeout.connect(self.click) def start_clicking(self): # 获取点击次数和间隔时间 count = int(self.edit_count.text()) interval = int(self.edit_interval.text()) # 设置计时器的时间间隔 self.timer.setInterval(interval) # 开始计时器 self.timer.start() # 禁用开始按钮 self.btn_start.setEnabled(False) # 设置光标样式为等待 QApplication.setOverrideCursor(QCursor(QtCore.Qt.WaitCursor)) # 记录已经点击的次数 self.clicked_count = 0 def click(self): # 点击鼠标左键 QCursor().pos() QCursor().setPos(100, 100) QCursor().pos() QApplication.processEvents() # 增加已经点击的次数 self.clicked_count += 1 # 如果达到设定的点击次数,停止计时器并启用开始按钮 if self.clicked_count >= int(self.edit_count.text()): self.timer.stop() self.btn_start.setEnabled(True) # 设置光标样式为默认 QApplication.restoreOverrideCursor() if name == "main": app = QApplication([]) mouse_clicker = MouseClicker() mouse_clicker.show() app.exec_() 打包为可执行文件
05-15

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值