Codefroces 374 B Inna and Sequence (树状数组 || 线段树)
Inna and Sequence
题意:先给你一个n,一个m, 然后接下来输入m个数,表示每次拳击会掉出数的位置,然后输入n个数,每次输入1或0在数列的末尾加上1或0,如果输入-1,相应m序列的数的位置就会掉出来并且后面的数会向前补位(每次删除操作可以看作是同时进行的,只有删除结束之后才会进行补位),最后输出这个数列的剩下结果,如果数列为空就输出“Poor stack!”。
题解:一开始想到的思路还是和上次CF889F想到的一样,在删除的位置标记一下,然后每次2分去查找在前面删除操作之后现在需要删除的位置对应哪里,然后再进行相应的位置,注意的就是,要么从后往前删除,且用二分去查找开始的第一个点,因为m序列如果太大的话,每次删除都会有很多时间浪费在不能删除的位置上; 要么就是先把每次对应的全部位置找出来,再进行删除,因为每次删除都会对后面的删除产生影响。
1 #include<cstdio> 2 using namespace std; 3 const int N = 1e6+10; 4 int n, m, R; 5 int tree[N], a[N]; 6 int ans[N]; 7 int lowbit(int x) 8 { 9 return x&(-x); 10 } 11 void Add(int x) 12 { 13 while(x <= n) 14 { 15 tree[x]++; 16 x += lowbit(x); 17 } 18 } 19 int Query(int x) 20 { 21 int ret = 0; 22 while(x > 0) 23 { 24 ret += tree[x]; 25 x -= lowbit(x); 26 } 27 return ret; 28 } 29 int Find_pos(int pos)//找到以前删除之后的补位之后的对应位置 30 { 31 int l = pos, r = R; 32 while(l <= r) 33 { 34 int mid = l+r >> 1; 35 int num = Query(mid); 36 if(mid == num + pos && ans[mid] != -1) 37 { 38 return mid; 39 } 40 else if(mid < num+ pos) l = mid+1; 41 else r = mid - 1; 42 } 43 } 44 void Delete() 45 { 46 int l = 1, r = m; 47 int len = R - Query(R); 48 while(l <= r)//2分寻找每次开始删除的位置,倒着删除 49 { 50 int mid = l + r >> 1; 51 if(len >= a[mid]) l = mid+1; 52 else r = mid-1; 53 } 54 for(int i = l-1; i > 0; i--) 55 { 56 int pos = Find_pos(a[i]); 57 ans[pos] = -1; 58 Add(pos); 59 } 60 } 61 int main() 62 { 63 scanf("%d%d",&n,&m); 64 for(int i = 1; i <= m; i++) 65 scanf("%d",&a[i]); 66 R = 0; 67 int t; 68 for(int i = 1; i <= n; i++) 69 { 70 scanf("%d",&t); 71 if(t == -1) 72 Delete(); 73 else ans[++R] = t; 74 } 75 if(R - Query(R) == 0) printf("Poor stack!\n"); 76 else 77 { 78 for(int i = 1; i <= R; i++) 79 { 80 if(ans[i] == -1) continue; 81 printf("%d",ans[i]); 82 } 83 } 84 return 0; 85 }
然后上面那个思路竟然被队长说TLE, 然后最后修改了n发,最终走到了和下面这个版本耗时差不多的慢几十ms的线段树版本操作。
开一棵线段树保存有效长度,然后每次通过有效长度去删除就好了。同时可以将m序列转化成有效长度,将删除数组的每一位减去前面的个数,就可以从头开始进行删除操作,
因为你每删除一次数据,有效长度就会减一,所以如果轮到第m个数,那么对于刚开始删除的序列的有效长度就已经减去m-1了。
1 #include<cstdio> 2 #define lson l,m,rt<<1 3 #define rson m+1,r,rt<<1|1 4 using namespace std; 5 const int N = 1e6+10; 6 int n, m; 7 int tree[N<<2], ans[N], Delt[N]; 8 void Add(int L,int C,int l, int r, int rt) 9 { 10 tree[rt]++; 11 if(l == r) 12 { 13 ans[l] = C; 14 return ; 15 } 16 int m = l+r >> 1; 17 if(L <= m) Add(L,C,lson); 18 else Add(L,C,rson); 19 } 20 void Delete(int Num, int l, int r, int rt) 21 { 22 tree[rt]--; 23 if(l == r) 24 { 25 ans[l] = -1; 26 return ; 27 } 28 int m = l+r >> 1; 29 if(tree[rt<<1] >= Num) Delete(Num, lson); 30 else Delete(Num-tree[rt<<1],rson); 31 } 32 int main() 33 { 34 //ios::sync_with_stdio(false); 35 //cin.tie(0); 36 //cout.tie(0); 37 scanf("%d%d",&n,&m); 38 for(int i = 0; i < m; i++) 39 { 40 scanf("%d",&Delt[i]); 41 Delt[i] -= i;//将位置转化成长度 42 } 43 int tot = 0; 44 int tmp; 45 for(int i = 1; i <= n; i++) 46 { 47 scanf("%d",&tmp); 48 if(tmp != -1) 49 Add(++tot,tmp,1,n,1); 50 else 51 { 52 for(int i = 0; i < m; i++) 53 { 54 if(tree[1] < Delt[i]) break; 55 else Delete(Delt[i],1,n,1); 56 } 57 } 58 } 59 if(tree[1] == 0) printf("Poor stack!\n"); 60 else 61 { 62 for(int i = 1; i <= tot; i++) 63 if(ans[i] != -1) 64 printf("%d",ans[i]); 65 } 66 return 0; 67 }