链接
题意
T组样例。
整数X起初为1,给出Q个询问和一个整数M,对每个询问,如果是(1, n),则将X更新为其乘n余M的结果并输出,如果是(2, n),则将第n个询问中的乘数从X中除去,更新并输出结果。
思路
主要问题在于第2种操作。
由于整个过程是不停在取模的,所以不能将X直接除以第n个询问的乘数因子。我们对操作建立线段树,则对询问(2, n),我们只要将第n个操作的因子变为1,再合并得到新的总因子,就能把那个乘数撤销掉了。
能这么做的关键在于乘法、取模运算是“叠加”的,在线段树区间进行合并的时候,直接把两个子区间的乘数因子乘起来取模就好。如果存在多种运算,并且运算之间的关系不容易叠加,就不能利用线段树区间合并的优势进行维护了。
代码
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long lint;
#define maxn (100010)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
lint seg[maxn << 2], mod;
void build(int l, int r, int rt)
{
seg[rt] = 1;
if(l == r) return ;
int m = (l + r) >> 1;
build(lson), build(rson);
}
void update(int i, int a, int l, int r, int rt)
{
if(l == r) { seg[rt] = a % mod; return; }
int m = (l + r) >> 1;
if(i <= m) update(i, a, lson);
else update(i, a, rson);
seg[rt] = (seg[rt<<1] * seg[rt<<1|1]) % mod;
}
lint query(int L, int R, int l, int r, int rt)
{
if(L <= l && r <= R) { return seg[rt]; }
int m = (l + r) >> 1, ret = 1;
if(L <= m) ret = ret * query(L, R, lson) % mod;
if(R > m) ret = ret * query(L, R, rson) % mod;
return ret;
}
int main()
{
//freopen("5475.txt", "r", stdin);
int T, kase = 0;
cin >> T;
while(T--)
{
printf("Case #%d:\n", ++kase);
int Q, M;
cin >> Q >> M;
mod = M;
build(1, Q, 1);
for(int i = 1, op, x; i <= Q; i++)
{
scanf("%d%d", &op, &x);
if(op == 1) update(i, x, 1, Q, 1);
else update(x, 1, 1, Q, 1);
printf("%I64d\n", query(1, i, 1, Q, 1));
}
}
return 0;
}