题目
题目背景
stoorz 有一个可重集。
有一天他突然发现一个惊人的消息:这个可重集,它居然是可重的!!!
所以他出了这道题。
题目描述
有一个可重集,初始为空集。
有 n n n 个操作,每个操作均属于以下两种类型之一:
1 x
:往可重集里加入一个正整数 x x x。2 x
:在可重集中删除数字 x x x。如果数字 x x x 出现了多次只删除一次。如果可重集内没有数字 x x x,那就忽略这次操作。有 m m m 次询问。每次询问给出两个数 l , r l,r l,r,你需要回答如果依次执行第 l l l 至第 r r r 个操作,最后可重集内的数的乘积 mod p \text{mod } p mod p 的值。特别的,我们定义空集的乘积为 1 1 1。
输入格式
第一行三个正整数 n , m , p n,m,p n,m,p,分别表示操作的数量,询问的次数,模数。
第二行至第 n + 1 n+1 n+1 行,每行两个正整数 o p t , x opt,x opt,x,表示这次操作的类型,以及操作的数 x x x。
最后 m m m 行,每行两个正整数 l , r l,r l,r,表示一次询问。
输出格式
输出 m m m 行,其中第 i i i 行表示第 i i i 次询问的答案。
输入输出样例
输入 #1
7 4 37 1 5 1 7 1 7 1 6 2 7 2 5 1 8 1 7 2 5 4 7 2 6
输出 #1
3 5 11 5
输入 #2
见附件 example2.in 该样例满足 Subtask 2 的特殊限制。
输出 #2
见附件 example2.ans
说明/提示
样例解释:对于 4 4 4 次询问询问,操作后他们的可重集分别如下:
{ 7 , 6 , 8 } , { 7 , 6 } , { 6 , 8 } , { 7 , 6 } \{7,6,8\},\{7,6\},\{6,8\},\{7,6\} {7,6,8},{7,6},{6,8},{7,6}
本题采用捆绑测试。
子任务编号 分值 n , m ≤ n,m\leq n,m≤ 特殊限制 1 1 1 20 p t s 20\rm pts 20pts 1 0 3 10^3 103 无 2 2 2 20 p t s 20\rm pts 20pts 3 × 1 0 5 3\times 10^5 3×105 p p p 是质数且所有操作的 x < p x<p x<p,所有询问的 l = 1 l=1 l=1 3 3 3 15 p t s 15\rm pts 15pts 3 × 1 0 5 3\times 10^5 3×105 p p p 是质数,所有询问的 l = 1 l=1 l=1 4 4 4 20 p t s 20\rm pts 20pts 3 × 1 0 5 3\times 10^5 3×105 所有询问的 l = 1 l=1 l=1 5 5 5 25 p t s 25\rm pts 25pts 3 × 1 0 5 3\times 10^5 3×105 无 对于 100 % 100\% 100% 的数据, n , m ≤ 3 × 1 0 5 n,m\leq 3\times 10^5 n,m≤3×105, 1 ≤ p ≤ 1 0 9 1\leq p\leq 10^9 1≤p≤109,每次操作满足 o p t ∈ { 1 , 2 } opt\in\{1,2\} opt∈{1,2}, 1 ≤ x ≤ 1 0 9 1\leq x\leq 10^9 1≤x≤109,每次询问满足 1 ≤ l ≤ r ≤ n 1\leq l\leq r\leq n 1≤l≤r≤n。
附件下载
example2.zip113.69KB
思路
对于原操作序列,每个删除操作对应的添加操作是一定的.
我们求出删除操作对应的添加操作,设操作 i i i对应操作 j j j( j < i , o p t i = 2 , o p t j = 1 j<i , opt_i=2 , opt_j=1 j<i,opti=2,optj=1).
设一个数组 a n a_n an(初始为1) , 询问按照右端点排序后从左向右扫描,同时,我们用指针 j j j遍历操作,若 o p t j = 1 opt_j=1 optj=1,则 a j = x j a_j=x_j aj=xj,若 o p t j = 2 opt_j=2 optj=2且 j j j有对应的操作,则 a p r e j = 1 a_{pre_j}=1 aprej=1.
对于一个查询 l , r l,r l,r, ( ∏ k = l r a k ) m o d p (\prod^r_{k=l}a_k) \bmod p (∏k=lrak)modp就是该查询的答案.
序列 a a a可用单点修改,区间查询的线段树维护.
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <stack>
#include <vector>
#define int long long
using namespace std;
using ll = long long;
int read() {
int re = 0;
char c = getchar();
bool negt = false;
while(c < '0' ||c > '9')
negt |= (c == '-') , c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return negt ? -re : re;
}
const int N = 3e5 + 10 , M = 3e5 + 10;
struct Query {
int l , r;
int id;
} query[M];
bool cmp(Query a , Query b) {
return a.r < b.r;
}
int n , m , mod;
int opt[N] , x[N];
int pre[N];
int ans[N];
unordered_map <int , int> mp;
vector <stack<int>* > stk;
struct SegmentTree {
struct Node {
int l , r;
int ls , rs;
ll dat;
Node() {
dat = 1;
}
} node[N * 4];
#define ls(_) node[_].ls
#define rs(_) node[_].rs
int newnode() {
static int cnt = 0;
return ++cnt;
}
int build(int l , int r) {
int id = newnode();
node[id].l = l , node[id].r = r , node[id].dat = 1;
if(l != r) {
int mid = (l + r) / 2;
node[id].ls = build(l , mid);
node[id].rs = build(mid + 1 , r);
}
return id;
}
void change(int root , int pos , int dat) {
if(node[root].l == node[root].r) {
node[root].dat = dat;
return ;
}
if(pos <= (node[root].l + node[root].r) / 2)
change(ls(root) , pos , dat);
else change(rs(root) , pos , dat);
node[root].dat = node[ls(root)].dat * node[rs(root)].dat % mod;
}
int query(int root , int l , int r) {
if(r < node[root].l || l > node[root].r)return 1;
if(l <= node[root].l && r >= node[root].r)return node[root].dat;
return query(ls(root) , l , r) * query(rs(root) , l , r) % mod;
}
#undef ls
#undef rs
} segT;
int root;
signed main() {
n = read() , m = read() , mod = read();
for(int i = 1 ; i <= n ; i++)
opt[i] = read() , x[i] = read();
for(int i = 1 ; i <= m ; i++)
query[i].l = read() , query[i].r = read() , query[i].id = i;
sort(query + 1 , query + m + 1 , cmp);
for(int i = 1 ; i <= n ; i++) {
int id;
if(mp.find(x[i]) == mp.end())
id = mp[x[i]] = stk.size() , stk.push_back(new stack<int>);
else
id = mp[x[i]];
auto s = stk[id];
if(opt[i] == 1) {
s->push(i);
} else if(s->size() > 0) {
pre[i] = s->top() , s->pop();
}
}
root = segT.build(1 , n);
for(int i = 1 , j = 1 ; i <= m ; i++) {
while(j <= n && j <= query[i].r) {
if(opt[j] == 1)
segT.change(root , j , x[j]);
else if(pre[j] > 0)
segT.change(root , pre[j] , 1);
++j;
}
ans[query[i].id] = segT.query(root , query[i].l , query[i].r);
}
for(int i = 1 ; i <= m ; i++)
printf("%lld\n" , ans[i]);
for(auto i : stk)delete i;
return 0;
}