1. 程式人生 > >Codefroces 374 B Inna and Sequence (樹狀數組 || 線段樹)

Codefroces 374 B Inna and Sequence (樹狀數組 || 線段樹)

pos %d 線段樹 blog and code 最終 namespace owb

Inna and Sequence

題意:先給你一個n,一個m, 然後接下來輸入m個數,表示每次拳擊會掉出數的位置,然後輸入m個數,每次輸入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 }

Codefroces 374 B Inna and Sequence (樹狀數組 || 線段樹)