code+12月月赛 火锅盛宴
时间限制: 2.0 秒
空间限制: 512 MB
题目背景
SkyDec和YJQQQAQ都是Yazid的好朋友。他们都非常喜欢吃火锅。有一天,他们聚在一起,享受一场火锅盛宴。
题目描述
在这场火锅盛宴中,有一个麻辣浓汤锅底的火锅和n种食物,每种食物数量都是无限的。我们用1至n将这些食材编号。
每种食物煮熟所需要的时间不同,第i种食物煮熟需要si单位时间。这表示如果你在第T个时刻将一个食物i下到火锅里,那么它会在第T+si个时刻被煮熟,并且此后一直会延续被煮熟的状态,直到它被拿走为止。
Yazid和YJQQQAQ的口味不同:YJQQQAQ觉得所有食物的好吃程度都是相同的;而Yazid则觉得没有两种食材的好吃程度是相同的,并且,巧合的是,编号越小的食物Yazid越喜欢吃。可怜的SkyDec由于不能吃辣,所以只能帮Yazid和YJQQQAQ煮食物。
整个火锅盛宴持续109单位时间。在整个盛宴中,三位好朋友除了谈笑风生之外,最重要的事当然就是吃东西了。在任意整数时刻,都有可能发生下列4种事件中的任意一种,我们用0至3之间的整数op描述事件类型:
0 id
:表示SkyDec往火锅里下了一个编号为id的食物。1
:Yazid在锅内搜寻熟了的且最喜欢吃的食物,并拿走一个这种食物。特别地,如果锅里没有熟了的食物,那么Yazid会很愤怒。2 id
:YJQQQAQ在锅内搜寻编号为id的食物:如果锅里不存在该种食物,则YJQQQAQ会很愤怒;如果锅里存在熟了的该食物,则YJQQQAQ会取走一个并食用;如果锅里只有未煮熟的该种食物,那么YJQQQAQ会希望知道最接近煮熟的该种食物(即锅内存在时间最长的该种食物)还需要多少时间被煮熟。3 l r
:馋涎欲滴的SkyDec想知道,锅里编号在[l,r]之间的且熟了的食物总共有多少个。
整个火锅晚宴中共发生了Q个事件,且没有任意两个事件在同一时刻发生。
他们的好朋友Flvze想知道这场火锅晚宴中发生的所有事,所以请你告诉她。
输入格式
从标准输入读入数据。
本题包含多组数据,输入的第一行为一个正整数T,表示数据组数。接下来依次描述每组数据,对于每组数据:
第一行一个正整数n,表示食物的种类数。
第二行n个用空格隔开的正整数s1,s2,…,sn,描述每种食物煮熟需要的时间。
第三行一个正整数Q,表示事件的数目。
接下来Q行,每行若干个用空格隔开的非负整数,描述一个事件。先是两个整数t,op,分别表示发生事件的时间以及事件的类型。如果op=0或op=2,则接下来1个正整数id,意义见题目描述
;如果op=1,则接下来没有其他数;如果op=3,则接下来2个正整数l,r,意义见题目描述
。
我们保证t按输入顺序严格递增。
我们保证1≤t≤109,0≤op≤3,1≤id≤n,1≤l≤r≤n。
输出格式
对于每个op≠0的操作,输出一行表示答案。对于不同的op,需要输出的内容如下:
- 对于op=1,如果Yazid成功取走食物,则输出他取走食物的编号;否则输出"Yazid is angry."(不含引号,下同)。
- 对于op=2,如果YJQQQAQ成功取走食物,则输出"Succeeded!";否则,如果锅里有未煮熟的该类食物,输出最接近煮熟的该种食物还需要多少时间被煮熟;否则,输出"YJQQQAQ is angry."。
- 对于op=3,输出锅内编号在指定范围内的熟食的数量。
输出到标准输出。
样例1输入
1
2
1 100
10
1 0 2
2 0 1
3 2 1
4 2 2
5 2 1
200 0 1
201 3 1 2
202 1
203 1
204 1
样例1输出
Succeeded!
97
YJQQQAQ is angry.
2
1
2
Yazid is angry.
子任务
测试点编号 | n≤ | Q≤ | 特殊约定 | 测试点分值 |
---|---|---|---|---|
1 | 500 | 1000 | 无 | 8 |
2-3 | 10 | 300,000 | 无 | 6 |
4-5 | 100,000 | 所有si=1 | 8 | |
6-7 | 所有si都相同 | 11 | ||
8-9 | op≠3 | 7 | ||
10-11 | 无 | 12 | ||
12-13 | 500,000 | 2 |
对于所有数据,保证T≤4,保证n≤100,000,Q≤500,000,1≤si≤108。
大意:有N种食物,编号越小越好吃,三个操作:(1)往火锅里放食物(2)查找最喜欢的而且熟了的食物(3)输出编号在[L,R]之间的熟的食物的数量
大意只是大意,题目要求还有好多细节,请移步题目描述和输出格式。
题解:
这题确实是一个很有意思的数据结构题,考场上想开N+1个set搞一搞,但是没有时间写这题了。
当然,大多数人写的是三个set的方法
后来出题人说三个set能过,因为他没有卡。
正解是
先将食物按照熟的时间从小到大排序,这样在枚举时间的时候就方便找到该下锅的食物或者此时刚熟的食物了。
树状数组+链表。
在按时间顺序枚举事件的时候,用树状数组维护前 k 种食物中熟的个数,用链表维护已经放下但是没有熟的食物。
一个食物下锅后先放入链表,发现链表表头的食物熟了,就取出表头,更新树状数组,查找和拿出食物的时候在树状数组里操作就行了。
值得一提的是树状数组的upper_bound(v)操作:找出 最小的 大于v的前缀和,也是O(logN)的时间复杂度。详见代码。
贴一份高仿某位大神的代码(我几乎背下来的)
/* Welcome Hacking Wish You High Rating */ #include<iostream> #include<cstdio> #include<cstring> #include<ctime> #include<cstdlib> #include<algorithm> #include<cmath> #include<string> using namespace std; int read(){ int xx=0,ff=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')ff=-1;ch=getchar();} while(ch>='0'&&ch<='9'){xx=(xx<<3)+(xx<<1)+ch-'0';ch=getchar();} return xx*ff; } const int maxn=100010,maxm=500010; int N,Q,s[maxn],arg1[maxm],arg2[maxm],t[maxm],op[maxm]; int id[maxm],cnt,head[maxn],tail[maxn]; struct BIT{ int b[maxn]; inline int lowbit(int x) {return x&-x;} void clear() {memset(b,0,sizeof(b));} void upd(int x,int v){ for(;x<=N;x+=lowbit(x)) b[x]+=v; } int query(int x){ int re=0; for(;x;x-=lowbit(x)) re+=b[x]; return re; } int upper_bound(int v){ int x=0,sum=0; for(int i=(1<<16);i;i>>=1) if(x+i<=N&&sum+b[x+i]<=v) sum+=b[x+=i]; return x+1; } }bit; bool mycmp(int xx,int yy) {return t[xx]+s[arg1[xx]]<t[yy]+s[arg1[yy]];} struct Node{ int data,next; }node[maxm]; int node_cnt; void add(int x,int val){ if(!head[x]) head[x]=tail[x]=++node_cnt; else{ node[tail[x]].next=++node_cnt; tail[x]=node_cnt; } node[node_cnt].data=val; } void remove(int x){ bit.upd(x,1); head[x]=node[head[x]].next; if(!head[x]) tail[x]=0; } int main(){ //freopen("in","r",stdin); //freopen("out","w",stdout); for(int T=read();T;T--){ N=read();cnt=0;node_cnt=0,bit.clear(); memset(head,0,sizeof(head)); memset(tail,0,sizeof(tail)); memset(node,0,sizeof(node)); for(int i=1;i<=N;i++) s[i]=read(); Q=read(); for(int i=1;i<=Q;i++){ t[i]=read(),op[i]=read(); if(op[i]==0)arg1[i]=read(),id[++cnt]=i; else if(op[i]==1); else if(op[i]==2)arg1[i]=read(); else arg1[i]=read(),arg2[i]=read(); } sort(id+1,id+1+cnt,mycmp); int q=1; for(int i=1;i<=Q;i++){ while(q<=cnt&&t[id[q]]+s[arg1[id[q]]]<=t[i]){ remove(arg1[id[q]]); q++; } if(op[i]==0) add(arg1[i],t[i]+s[arg1[i]]); else if(op[i]==1){ int temp=bit.upper_bound(0); if(temp==N+1) printf("Yazid is angry.\n"); else{ bit.upd(temp,-1); printf("%d\n",temp); } } else if(op[i]==2){ if(bit.query(arg1[i])-bit.query(arg1[i]-1)){ bit.upd(arg1[i],-1); printf("Succeeded!\n"); } else if(head[arg1[i]]) printf("%d\n",node[head[arg1[i]]].data-t[i]); else printf("YJQQQAQ is angry.\n"); } else printf("%d\n",bit.query(arg2[i])-bit.query(arg1[i]-1)); } } return 0; }