Codefroces 374 B Inna and Sequence (树状数组 || 线段树)

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 }

原文地址:https://www.cnblogs.com/MingSD/p/8424377.html

时间: 2024-10-16 10:13:58

Codefroces 374 B Inna and Sequence (树状数组 || 线段树)的相关文章

hdu 1166 树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 51177    Accepted Submission(s): 21427 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务

hdu1394(枚举/树状数组/线段树单点更新&amp;区间求和)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 或 -1, 交换两个不相邻数 a, b, 逆序数 += 两者间大于 a 的个数 - 两者间小于 a 的个数: 所以只要求出初始时的逆序对数,就可以推出其余情况时的逆序对数.对于求初始逆序对数,这里 n 只有 5e3,可以直接暴力 / 树状数组 / 线段树 / 归并排序: 代码: 1.直接暴力 1

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 1166 敌兵布阵 (树状数组&#183;线段树)

题意  中文 动态区间和问题   只会更新点  最基础的树状数组 线段树的应用 树状数组代码 #include <bits/stdc++.h> using namespace std; const int N = 50005; int c[N], n, m; void add(int p, int x) { while(p <= n) c[p] += x, p += p & -p; } int getSum(int p) { int ret = 0; while(p > 0

Curious Robin Hood(树状数组+线段树)

1112 - Curious Robin Hood    PDF (English) Statistics Forum Time Limit: 1 second(s) Memory Limit: 64 MB Robin Hood likes to loot rich people since he helps the poor people with this money. Instead of keeping all the money together he does another tri

士兵杀敌(四)(树状数组+线段树)

士兵杀敌(四) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描述 南将军麾下有百万精兵,现已知共有M个士兵,编号为1~M,每次有任务的时候,总会有一批编号连在一起人请战(编号相近的人经常在一块,相互之间比较熟悉),最终他们获得的军功,也将会平分到每个人身上,这样,有时候,计算他们中的哪一个人到底有多少军功就是一个比较困难的事情,军师小工的任务就是在南将军询问他某个人的军功的时候,快速的报出此人的军功,请你编写一个程序来帮助小工吧. 假设起始时所有人的军功都是0. 输入

Color the ball(树状数组+线段树)

Color the ball Time Limit : 9000/3000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 3   Accepted Submission(s) : 1 Problem Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气球b

Codeforces Round #225 (Div. 1) C 树状数组 || 线段树

看到这题很开心啊,有印象跟以前做过的很像,貌似最近就做过一个,以时间戳为区间来建立树状数组,然后一开始我以为题意是,给x点加val,它以下的所有节点都加-val:所以一开始就以 加 和 减 建立了两个树状数组,最后 减去就是答案,写完发现跟案例对不上啊,读了题目也没发现读错了,对于那句话 我理解错了,后来看了 这个: http://blog.csdn.net/keshuai19940722/article/details/18967661 仔细看看处理部分,我还以为分奇偶性有规律呢,后来才发现读

HDU 4417 Super Mario (树状数组/线段树)

Super Mario Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Description Mario is world-famous plumber. His “burly” figure and amazing jumping ability reminded in our memory. Now the poor princess is in trouble agai

BZOJ 3211 花神游历各国 树状数组(线段树)+优化

题意:给你一段区间,然后每个点的初始值都告诉你,现有两种操作,一种是给你一个小区间的左右端点,之后把这个区间内的所有值都开根号,另一种就是区间求值. 方法:树状数组维护求和,巧妙开根号.(线段树) 解析:这道是某次考试考的题- -.当时也没想到快的开根号方法,暴力开根号好像70分吧. 首先要明确一个事情:被开根号的数最大是109,而109开几次会开到1呢?用计算器算一下发现5次就将这个最大的数开到1了,而1开根号怎么开都是1,所以如果再对其进行开根号操作,仅仅是无用的浪费时间了.所以怎么维护这种