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!
");
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!
");
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 }
原文地址:https://www.cnblogs.com/MingSD/p/8424377.html