奶茶计划
题目链接:ybtoj高效进阶 21176
题目大意
有 b 个东西,然后一开始你拿了 0~a 的东西,然后有几种操作:
拿一个东西,扔一个拿了的东西,把最先扔出的东西拿回来。
然后每次操作后和所有操作前问你第一个不在你手上的东西是哪个。
思路
考虑把买了的和没买的分开来搞,然后两个中选最小的。
没买的其实很好维护,哪个指针记录没买的中最小的,然后数据记录每个是否买了,每次修改之后维护一下指针即可。
然后买了的我们就是要在丢出的中支持删除最早的,以及查询最小值,不难想到可以用单调队列。
然后就好了。
代码
#include<queue>
#include<cstdio>
#include<iostream>
#define ll long long
#define mo 998244353
using namespace std;
int T, m, a, b, c, d, op;
int p[1000001];
unsigned int seed;
ll ans;
bool in[2000001], out[2000001];
int sta[1000001], staa[1000001], la, laa;
unsigned int randnum() {
seed = seed ^ (seed << 13);
seed = seed ^ (seed >> 17);
seed = seed ^ (seed << 5);
return seed;
}
void work3() {
//第三个操作
if (d) return ;
if (la > sta[0]) {
op = 4; return ;}
if (staa[laa] == sta[la]) {
laa++;
}
out[sta[la]] = 0;
la++;
}
int main() {
// freopen("knowledge.in", "r", stdin);
// freopen("knowledge.out", "w", stdout);
scanf("%d", &T);
while (T--) {
scanf("%d %u %d %d %d %d", &m, &seed, &a, &b, &c, &d);
for (int i = 1; i <= m; i++)
if (randnum() % c == 0) p[i] = -1;
else p[i] = randnum() % b;
ans = 0; sta[0] = 0; staa[0] = 0; la = laa = 1;
for (int i = 0; i <= a; i++) in[i] = 1, out[i] = 0;
for (int i = a + 1; i <= b; i++) in[i] = 0, out[i] = 0;
int noww = a + 1;
for (int i = 1; i <= m; i++) {
if (p[i] == -1) op = 3, work3();
else {
if (!in[p[i]]) {
in[p[i]] = 1; while (in[noww]) noww++; op = 1;}//操作一
else if (!out[p[i]] && !d) {
out[p[i]] = 1; sta[++sta[0]] = p[i]; while (laa <= staa[0] && p[i] <= staa[staa[0]]) staa[0]--; staa[++staa[0]] = p[i]; op = 2;}//操作二
else op = 3, work3();
}
if (d && (op != 1)) continue;
if (op == 4) continue;
ans ^= 1ll * min(noww, laa <= staa[0] ? staa[laa] : 1000000000) * (1ll * i * i % mo + 7ll * i % mo) % mo;
//第一个没买的和飞出中最小编号的中选最小的
}
printf("%lld\n", ans);
}
return 0;
}