题目链接:
题目大意:
一个计算器只有两种运算,初始化 X = 1
第一种操作: X 乘以 一个数,获得新的 X
第二种操作: 当前的 X 除以 一个数
输出 X % M
其中 1 y 表示第一种操作,即 X = X * y
2 n 表示 X 除以 前面的 第 n 个操作的那个 y,保证 第 n 个表示的是 1,即数据合法
注意:X 在进行操作的时候不要除模, 而是输出的时候除模,即要保证 X 进行操作的时候除模是和不除模情况下是相等的。
比如:
3 10
1 5
1 3
2 1,如果边运算边除模,最后的答案是 5, 5, 0, 但是正确答案是 (1*5)%10,(1*5*3)%10, (1*5*3 / 5) % 10。
样例解释:
1 // 测试样例组数
10 1000000000 // 10个操作,M
1 2 // 1 * 2 = 2
2 1 // 1 * 2 / 2 = 1 除以第一个操作是 1
1 2 // 1 * 2 = 2
1 10 // 2 * 10 = 20
2 3 // 20 / 2 = 10
2 4
1 6
1 7
1 12
2 7
解题思路:
最开始的想法肯定是暴力。把每次操作都记下来,乘法就是 从第一个数 边乘 边取模输出。除法就是把 相应的前面的数变为 1 即可,在从第一个数 边乘边取模。
但无意 是TLE。
我的做法是 用 线段树来做。线段树的节点保存 这个区间 所有数相乘 取模 后的值。
且线段树 初始化全为 1 ,乘法就把相应的 值 改为 y,除法就把相应的 值 改为 1
每次询问 都输出 tree[1].num 即可, 要注意 long long 哟
参考代码:
#include <stdio.h> #include <iostream> #include <string.h> using namespace std; #define manx 100005 struct Tree { int left; int right; long long num; //该区间内,已经出现的个数 }tree[4 * manx]; int a[manx], n, m; void BuildTree(int i, int l, int r); void Update(int i, int id, int val); int Query(int i, int l, int r); int main() { int T, x, y; scanf("%d", &T); for(int tt = 1; tt <= T; tt++) { scanf("%d %d", &n, &m); BuildTree(1, 1, n); printf("Case #%d: ", tt); for(int i = 1; i <= n; i++) { scanf("%d %d", &x, &y); if(x == 1) { Update(1, i, y); printf("%lld ", tree[1].num); } else { Update(1, y, 1); printf("%lld ", tree[1].num); } } } return 0; } void BuildTree(int i, int l, int r)//建树 { tree[i].left = l; tree[i].right = r; tree[i].num = 1;//初始化全部为0,因为还没有输入 if(tree[i].left == tree[i].right) return; int mid = (l + r) >> 1; BuildTree(i << 1, l, mid); BuildTree(i << 1 | 1, mid + 1, r); } void Update(int i, int id, int val)//更新 { if(tree[i].left == id && tree[i].right == id) { tree[i].num = val;//输入过了,标记为1 return; } int mid = (tree[i].left + tree[i].right) >> 1; if(id > mid) Update(i << 1 | 1, id, val); else Update(i << 1, id, val); tree[i].num = (tree[i << 1].num * tree[i << 1 | 1].num) % m;//更新此区间内,已经出现过的总数 }