记录40
#include<bits/stdc++.h>
using namespace std;
struct node{
int num;
string name;
};
int main(){
int n,m,d,p;
cin>>n>>m;
node a[100010]={};
for(int i=0;i<n;i++) cin>>a[i].num>>a[i].name;
int t=0;
string str;
while(m--){
cin>>d>>p;
if(a[t].num==d){//注意逆时针,判断朝向跟左右手
t=(t-p+n)%n;
str=a[t].name;
}
else{
t=(t+p)%n;
str=a[t].name;
}
}
cout<<str;
return 0;
}
题目传送门
https://www.luogu.com.cn/problem/P1563
突破点
这个谜题中玩具小人的朝向非常关键,因为朝内和朝外的玩具小人的左右方向是相反的:面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向;而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向。
以逆时针为顺序给出每个玩具小人的朝向和职业。其中 0 表示朝向圈内,1 表示朝向圈外。保证不会出现其他的数。字符串长度不超过 10 且仅由英文字母构成,字符串不为空,并且字符串两两不同。
两个整数 ai,si,表示第 i 条指令。若 ai=0,表示向左数 si 个人;若 ai=1,表示向右数 si 个人
思路
- 每个小人带两个数据👉结构体
- 对圈的放行进行判断👉面朝圈的方向+左右手
- 围着圈进行移动👉取余的处理手法
代码简析
#include<bits/stdc++.h>
using namespace std;
struct node{
int num;
string name;
};
int main(){
int n,m,d,p;
cin>>n>>m;
node a[100010]={};
for(int i=0;i<n;i++) cin>>a[i].num>>a[i].name;
int t=0;
string str;
...
return 0;
}
将小人的情况输入进去
for循环从编号0开始,方便后面进行圈内循环(数组实现循环)
int t=0; t代表目前的小人数
string str; str存小人的名字
#include<bits/stdc++.h>
using namespace std;
struct node{
int num;
string name;
};
int main(){
int n,m,d,p;
cin>>n>>m;
node a[100010]={};
for(int i=0;i<n;i++) cin>>a[i].num>>a[i].name;
int t=0;
string str;
while(m--){
cin>>d>>p;
if(a[t].num==d){//注意逆时针,判断朝向跟左右手
t=(t-p+n)%n;
str=a[t].name;
}
else{
t=(t+p)%n;
str=a[t].name;
}
}
cout<<str;
return 0;
}
d是手的左右方向
p是移动几个人
因为是逆时针,圈外网左,圈内往右,其实是一个方向
取余其实就是一种循环操作
五个人编号:0 1 2 3 4 组成一个圈
以编号二为例
逆时针转动三个人 (2-3+5)%5
顺时针转动三个人 (2+3+5)%5
补充
取余运算的"圈内循环"特性详解
1. 核心机制:有限状态自动回归
取余运算的本质是将无限整数域映射到有限集合
[0, mod-1],形成闭环循环。for (int i = 0; i < 20; i++) { cout << i % 5 << " "; // 输出:0 1 2 3 4 0 1 2 3 4 0 1 2 3 4... }特点:无论
i增长到多大,结果永远在[0,4]这5个状态内循环,如同钟表的指针。
2. 数学原理:模运算的周期性
对任意整数
a和正整数mod,满足:a % mod ∈ [0, mod-1] (a + mod) % mod = a % mod // 周期为mod几何意义:数轴被折叠成
mod个点的圆圈,每前进mod步回到原点。
3. 竞赛七大应用场景
场景1:数组环形访问(最高频)
int a[5] = {10,20,30,40,50}; int idx = 7; // 越界索引 // 循环访问:自动回到数组开头 cout << a[idx % 5]; // 输出40(等价于a[2]) // 通用模板:循环队列 int front = 0, rear = 0, q[100]; rear = (rear + 1) % 100; // 队尾循环移动场景2:哈希散列(冲突处理)
// 线性探测法:冲突时循环向后找空位 int hash_table[MOD]; int pos = key % MOD; while (hash_table[pos] != 0) { pos = (pos + 1) % MOD; // 循环探测,不越界 }场景3:随机数生成(循环采样)
// 在游戏或模拟中循环遍历玩家 int current_player = 0; while (game_running) { current_player = (current_player + 1) % n; // 循环轮到下一个玩家 // 处理current_player的行动 }场景4:状态压缩与循环移位
int state = 7; // 二进制111 int rotated = (state << 1) | (state >> 2); // 循环左移 // 取余保证位数不变:rotated &= (1<<3)-1 = 7场景5:周期性事件调度
// 7天为周期的任务 int day_of_week = (start_day + offset) % 7; // 0=周日,...,6=周六 // 月份循环(配合数组) int days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; int next_month = (current_month + 1) % 12; // 12月后回到1月场景6:进制转换与循环进位
// 大数取模:快速计算10^n mod m long long power_mod(long long n, long long m) { long long ans = 1; for (int i = 0; i < n; i++) { ans = (ans * 10) % m; // 每次循环都在[0,m-1]内 } return ans; }场景7:循环计数器与索引
// 统计字符出现次数(循环映射到数组) char c; int cnt[26] = {0}; cin >> c; if (c >= 'a' && c <= 'z') { cnt[c - 'a']++; // 隐式取余:字母循环映射到0-25 } // 显式取余版本:处理循环输入 for (int i = 0; i < n; i++) { int bucket = i % 26; // 循环使用26个桶 // 处理bucket }
4. 负数取余的陷阱与修复
C++负数取余行为
-7 % 3 = -1 // 余数符号与被除数一致 7 % -3 = 1 // 余数符号与被除数一致问题:负余数不在
[0, mod-1]的"圈内",破坏循环性。标准化修复
// 通用模板:将余数转换到[0, mod-1] int mod = 5; int x = -7; int normalized = (x % mod + mod) % mod; // 结果:3 // 竞赛宏定义 #define MOD(a, m) ((a) % (m) + (m)) % (m)
5. 竞赛高频技巧与模板
技巧1:快速判断循环重合点
// 两指针以不同速度循环移动,必在mod步内相遇 int slow = 0, fast = 0; do { slow = (slow + 1) % mod; fast = (fast + 2) % mod; } while (slow != fast); // Floyd判圈算法核心技巧2:循环差值计算
// 计算环形数组中两点距离(顺时针) int dist = (b - a + n) % n; // 无论b<a还是b>a都正确技巧3:倍增循环
// 快速幂的取余循环 long long pow_mod(long long a, long long b, long long mod) { long long ans = 1; while (b) { if (b & 1) ans = (ans * a) % mod; a = (a * a) % mod; b >>= 1; } return ans; }
6. 常见错误与调试
错误代码 问题 正确写法 a[-1] % 3数组越界 a[(i+mod-1)%mod]i % 0除零错误 确保 mod > 0cnt[hash % 1000]负哈希值 cnt[(hash % 1000 + 1000) % 1000]for(i=0; i<=n; i++) a[i%k]多循环一次 i < n不是i <= n
7. 核心要点总结
循环本质:
a % mod将整数映射到有限集合[0, mod-1]周期公式:
(a + k*mod) % mod = a % mod(k为任意整数)负数修复:
(x % mod + mod) % mod保证结果非负数组循环:
arr[i % n]是实现环形缓冲区的核心性能:取余运算 O(1),但比加减法慢,在极大量运算时可考虑位优化(
mod=2^k时用& (mod-1))边界:确保
mod > 0,否则触发运行时错误竞赛箴言:遇到"循环"、"轮转"、"周期"、"哈希冲突"等关键词,立刻想到取余运算。
1076

被折叠的 条评论
为什么被折叠?



