绍兴游记Day8 b 题解
Link
官方题目传送门 官方题解传送门
题意
Hzy有一个集合,一开始有[0dots a]这些数字(如果a=-1则说明集合为空)。接下来有m个时刻,每个时刻都会有一种操作。 1. 插入一个数字x,保证x不在集合中。 2. 删去一个数字x。 3. 把目前不在集合中的最早被删除的数字,插回到集合中(如果一个数字曾经被删去被插回来过然后再删去,这里认为其删去的时间为最近一次删去的时间)。 由于描述这m个时刻的操作实在太麻烦了,所以Hzy用了一个长度为m的序列p来描述每个时刻的操作种类。对于每个操作,满足以下约定。 1. 这个序列p里所有元素均为[-1,b)的整数 2. 若p_i=-1,则表示时刻i的操作为第三种,如果此时并不存在满足条件且被删去的数字,则忽略此操作。 3. 否则,如果时刻i中,大小为p_i的数字一开始不在集合中且也从来没有通过第一种操作插入集合中,则表示第i个操作为向集合中插入一个大小为p_i的数字,即第一种操作。 4. 否则,如果时刻i中,大小为p_i的数字在集合中,则把p_i从集合里删除,即第二种操作。 5. 否则,表示时刻i的操作为第三种,如果此时并不存在满足条件且被删去的数字,则忽略此操作。 Hzy现在想知道在第i个时刻的操作进行完后,集合的mex是什么,即在集合中未出现过的最小的自然数。第i个操作的答案设为ans_i(如果第i个操作被忽略,ans_i=0)。但是她不满足仅知道ans_i,她想知道ans_i×(i^2 7i) mod 998244353的异或和 如果某个时刻的操作被忽略,那么Hzy将不会进行任何操作,也不计算此时的答案。 下面是IO输入代码:
代码语言:javascript复制namespace IO{
int c;
unsigned int seed;
unsigned int randnum(){seed^=seed<<13;seed^=seed>>17;seed^=seed<<5;return seed;}
inline int read(int &x){scanf("%d",&x);return x;}
inline void init_case(int &m,int &a,int &b,int &d,int p[]){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;}}
inline void update_ans(unsigned int &ans_sum,unsigned int cur_ans,int no){const static unsigned int mod=998244353;ans_sum^=(long long)no*(no 7)%mod*cur_ans%mod;}
}
题解&Code
代码语言:javascript复制#include<bits/stdc .h>
using namespace std;
namespace IO{
int c;
unsigned int seed;
unsigned int randnum(){seed^=seed<<13;seed^=seed>>17;seed^=seed<<5;return seed;}
inline int read(int &x){scanf("%d",&x);return x;}
inline void init_case(int &m,int &a,int &b,int &d,int p[]){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;}}
inline void update_ans(unsigned int &ans_sum,unsigned int cur_ans,int no){const static unsigned int mod=998244353;ans_sum^=(long long)no*(no 7)%mod*cur_ans%mod;}
}
void write(int x){if(x<0) putchar('-'),x=-x;if(x<10) putchar(x '0');else{write(x/10);putchar(x '0');}}
unsigned int ans_sum;
int T,m,a,b,d,ans,p[4000010],f[4000010],vis[4000010],num[4000010],h,t,g[4000010];
bool did[4000010],tag;
queue<int> q;
namespace Solve{
inline void Step1(int x){vis[x]=1;did[x]=1;while(vis[ans]&&ans<=b) ans ;}//操作1:加入一个数字,把这个数标记在集合中(did)以及之前或现在加入过(vis),while求出范围内没有加入过的最小值
inline void Step2(int x){if(d==1){tag=1;return ;}did[x]=0;q.push(x);while(h<=t&&g[t]>x) t--;g[ t]=x;}//操作2:删除一个数,如果d=1那么忽略,否则将这个数取消在集合中这个标记(did)并且加入到"删除过的"队列中,再插入到删除的单调度队列
inline void Step3(){if(dq.empty()){tag=1;return ;}did[q.front()]=1;q.pop();}//操作3:加入最早删除的值,d=1、之前没有删除时忽略,否则把该数字标记在集合中,并且在队列中pop掉这个数
}
signed main(){
IO::read(T);//输入数据组数
while(T--){
IO::init_case(m,a,b,d,p);//调用IO读入
for(int i=0;i<=a;i ) vis[i]=1,did[i]=1;//初始化,一开始[0,a]区间内所有的数都在集合中
for(int i=a 1;i<=b;i ) vis[i]=0,did[i]=0;//初始化,一开始(a,b]区间内所有的数都不在集合中
ans=a 1;h=1;t=0;tag=0;ans_sum=0;//一开始ans=a 1(因为[0,a]区间内所有的数已经在集合中)
while(!q.empty()) q.pop();//清空队列
for(int i=1;i<=m;i ){
tag=0;//tag表示该操作是否忽略(0表示不忽略,1表示忽略)
if(p[i]==-1) Solve::Step3();//如果操作为-1,那么进行操作3
else if(vis[p[i]]==0) Solve::Step1(p[i]);//如果之前没有加入过该数,那么进行操作1
else if(did[p[i]]) Solve::Step2(p[i]);//如果现在还是有这个数,那么进行操作2
else Solve::Step3();//否则进行操作3
if(tag==1) continue ;//如果忽略,那就忽略
while(h<=t&&did[g[h]]) h ;//如果在集合中,那就下一个
if(h>t) IO::update_ans(ans_sum,ans,i);//如果队列里没有了,那么就是ans(加入操作中的最小值)
else IO::update_ans(ans_sum,min(ans,g[h]),i);//否则就是加入操作最小值与删除操作最小值的最小值
}
write(ans_sum);putchar('n');//输出最终结国
}
}
完结撒花✿✿ヽ(°▽°)ノ✿