CF-1028D Order book
订单簿() --CF-1028D --2100
时间限制:\(2000ms\) 空间限制:\(256MB\)
题目描述:
存在两个集合 \(A,B\),集合 \(A\) 可以每次取出集合中的最大值,集合 \(B\) 可以每次取出集合中的最小值。
有两种操作,都有给定参数 \(x\):
- ADD:往某一个集合中插入 \(x\);
- ACCEPT:从某一个集合中取出对应的值,且这个值为 \(x\)。
存在 \(n\) 次操作,每次给定操作类型和 \(x\)。
请有多少种合法的选择(即每一次操作选择哪个集合),可以使得整个操作序列合法。
若无解则输出 \(0\),保证所有给定的 \(x\) 互不相同。
输入描述:
第一行包含一个整数 \(n\) (\(1 \le n \le 363\,304\)) ,表示操作数。
接下来的 \(n\) 行中,每行包含一个字符串 ACCEPT 或者 ADD,以及一个整数 \(x\) (\(1 \le x \le 308\,983\,066\))。
输出描述:
能使所有操作都合法的方案数,答案对 \(10^9 + 7\) 取模。
样例输入输出:
| 输入1 | 输出1 | 输入2 | 输出2 | 输入3 | 输出3 | 
|---|---|---|---|---|---|
| 6 ADD 1 ACCEPT 1 ADD 2 ACCEPT 2 ADD 3 ACCEPT 3 | 8 | 4 ADD 1 ADD 2 ADD 3 ACCEPT 2 | 2 | 7 ADD 1 ADD 2 ADD 3 ADD 4 ADD 5 ACCEPT 3 ACCEPT 5 | 0 | 
样例解释:
在第一个例子中,每个元素都可以放入任意集合。
在第二个例子中,元素 \(1\) 必须放入集合 \(A\),元素 \(3\) 必须放入集合 \(B\).
思路
考虑维护 \(l,r\) 分别表示 \(A\) 集合合法最大值的最小值、\(B\) 集合合法最小值的最大值,规定 \(l\) 插入了 \(A\),\(r\) 插入了 \(B\)。
对于插入操作:
- 如果插入的数在 \(l\) 左侧或 \(r\) 右侧,则这个数所在集合是确定的;
- 否则这个数可能存在于两种集合。
对于取出操作:
- 如果取出的数在 \(l\) 左侧或 \(r\) 右侧,说明取出的数无法成为集合中的极值,可以判断无解;
- 否则,若 \(x\neq l\) 且 \(x\neq r\),说明 \(x\) 可以放入两个集合,答案翻倍;
- 如果 \(x=l\) 或 \(x=r\),直接取出即可。
每次取出后需要维护 \(l,r\),将 \(l\) 变为最大的小于 \(x\) 的数,将 \(r\) 变为最小的大于 \(x\) 的数。
更新 \(l,r\) 可以通过 set 维护。
同时,需要记录当前 \((l,r)\) 内待确定的数量。如果最终有 \(cnt\) 个数没有确定集合,那么答案需要乘上 \(cnt+1\),因为你可以在 \(cnt+1\) 个位置划分,将他们分别插入两个集合。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,l,r;
set<int>s;
int cnt,ans;
signed main()
{
	cin>>n; ans=1;
	l=-1e9,r=1e9;
	s.insert(l);
	s.insert(r);
	while(n--)
	{
		string opt;
		int x;
		cin>>opt>>x;
		if(opt=="ADD")
		{
			s.insert(x);
			if(x>l && x<r) cnt++;
		}
		else
		{
			cnt=0;
			if(x<l || x>r)
			{
				cout<<0<<endl;
				return 0;
			}
			if(x>l && x<r) ans=ans*2%mod;
			auto it=s.find(x);
			it--; l=*it;
			it++; it++;
			r=*it; it--;
			s.erase(it);
		}
	}
	ans=ans*(cnt+1)%mod;
	cout<<ans<<endl;
	return 0;
}
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号