可重集

T202677 可重集

目录

题目

题目背景

stoorz 有一个可重集。

有一天他突然发现一个惊人的消息:这个可重集,它居然是可重的!!!

所以他出了这道题。

题目描述

有一个可重集,初始为空集。

有 \(n\) 个操作,每个操作均属于以下两种类型之一:

  • 1 x:往可重集里加入一个正整数 \(x\)。
  • 2 x:在可重集中删除数字 \(x\)。如果数字 \(x\) 出现了多次只删除一次。如果可重集内没有数字 \(x\),那就忽略这次操作。

有 \(m\) 次询问。每次询问给出两个数 \(l,r\),你需要回答如果依次执行第 \(l\) 至第 \(r\) 个操作,最后可重集内的数的乘积 \(\text{mod } p\) 的值。特别的,我们定义空集的乘积为 \(1\)。

输入格式

第一行三个正整数 \(n,m,p\),分别表示操作的数量,询问的次数,模数。

第二行至第 \(n+1\) 行,每行两个正整数 \(opt,x\),表示这次操作的类型,以及操作的数 \(x\)。

最后 \(m\) 行,每行两个正整数 \(l,r\),表示一次询问。

输出格式

输出 \(m\) 行,其中第 \(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\) 次询问询问,操作后他们的可重集分别如下:

\[\{7,6,8\},\{7,6\},\{6,8\},\{7,6\} \]


本题采用捆绑测试。

子任务编号 分值 \(n,m\leq\) 特殊限制
\(1\) \(20\rm pts\) \(10^3\)
\(2\) \(20\rm pts\) \(3\times 10^5\) \(p\) 是质数且所有操作的 \(x<p\),所有询问的 \(l=1\)
\(3\) \(15\rm pts\) \(3\times 10^5\) \(p\) 是质数,所有询问的 \(l=1\)
\(4\) \(20\rm pts\) \(3\times 10^5\) 所有询问的 \(l=1\)
\(5\) \(25\rm pts\) \(3\times 10^5\)

对于 \(100\%\) 的数据,\(n,m\leq 3\times 10^5\),\(1\leq p\leq 10^9\),每次操作满足 \(opt\in\{1,2\}\),\(1\leq x\leq 10^9\),每次询问满足 \(1\leq l\leq r\leq n\)。

附件下载

example2.zip113.69KB

思路

对于原操作序列,每个删除操作对应的添加操作是一定的.

我们求出删除操作对应的添加操作,设操作\(i\)对应操作\(j\)(\(j<i , opt_i=2 , opt_j=1\)).

设一个数组\(a_n\)(初始为1) , 询问按照右端点排序后从左向右扫描,同时,我们用指针\(j\)遍历操作,若\(opt_j=1\),则\(a_j=x_j\),若\(opt_j=2\)且\(j\)有对应的操作,则\(a_{pre_j}=1\).

对于一个查询\(l,r\),\((\prod^r_{k=l}a_k) \bmod p\)就是该查询的答案.

序列\(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;
}
上一篇:【二分】【基础】(跳石头)(合并果子)


下一篇:10.3 国庆集训测试