SYSU-5, POJ 2131, 树状数组+二分

题目大意:给出n个人,顺序对位置进行请求,如果第i个人请求的位置上有人,则让这个人顺延,如果顺延的位置继续有人,递归进行,问最后所有人的位置。

解:这题貌似可以用平衡树+并查集搞定,但是我队友强烈安利树状数组的做法。赛场上没出,赛后结合discuz想了一下,作一下处理。

首先如果是一个请求第a[i]个有空位置的问题,那么这个问题显然可以用树状数组维护前缀和即可。所以我们现在考虑将原问题转化成这个问题。

考虑终态,把没有人的位置去掉,剩下的n个座位排在一起,显然转化成上面模型的形式

第i个询问时,如果我们记b[i] = 前i-1个询问里1~a[i]-1上有多少座位被请求,那么显然倒过来处理时,b[i]+1个位置即为所求位置(因为前面的人不会和后面的人抢位置,后面的人更优先确定,所以他的位置只取决于前面有多少空的,而他占掉座位后,如果前面的人请求了原意义上的同一位置,那么就在转化后的位置顺延即可,其实就是我们所提希望的转化模型,n个椅子,每次请求第b[i]个空椅子)

但是平衡树的做法真的很快(毕竟省一个二分的log),10s的时限我写了9.8s = =b,如果写完计算几何还有力气就琢磨一下平衡树的写法吧!

  1 #include <cstdio>
  2 #include <string>
  3 #include <iostream>
  4 #include <algorithm>
  5 #include <cmath>
  6 #include <cstring>
  7 #include <complex>
  8 #include <set>
  9 #include <vector>
 10 #include <map>
 11 #include <queue>
 12 #include <deque>
 13 #include <ctime>
 14
 15 using namespace std;
 16
 17 const double EPS = 1e-8;
 18
 19 #define ABS(x) ((x)<0?(-(x)):(x))
 20 #define SQR(x) ((x)*(x))
 21 #define MIN(a,b) ((a)<(b)?(a):(b))
 22 #define MAX(a,b) ((a)>(b)?(a):(b))
 23
 24 #define LSON(x) ((x)<<1)
 25 #define RSON(x) (((x)<<1)+1)
 26 #define LOWBIT(x) ((x)&(-(x)))
 27 #define MAXN 311111
 28 #define LL long long
 29 #define OO 214748364
 30
 31 int a[MAXN], b[MAXN], f[MAXN], fa[MAXN];
 32 int n, m;
 33
 34 struct TreeArray{
 35     int tree[MAXN], n;
 36     void clear(int lim = MAXN-10) {
 37         memset(tree, 0, sizeof(tree[0])*(lim+10));
 38         n = lim;
 39     }
 40     void add(int x, int num) {
 41         while (x <= n) {
 42             tree[x] += num;
 43             x += LOWBIT(x);
 44         }
 45     }
 46     int get(int x) {
 47         int res = 0;
 48         while (x) {
 49             res += tree[x];
 50             x -= LOWBIT(x);
 51         }
 52         return res;
 53     }
 54 } treeArray;
 55
 56 int get(int x) {
 57 //    return x == fa[x] ? x : fa[x] = get(fa[x]);
 58     int t = x; while (t != fa[t]) t = fa[t];
 59     for (int i = x, j; i != t; i = j) {
 60         j = fa[x]; fa[x] = t;
 61     }
 62     return t;
 63 }
 64
 65 int main() {
 66     freopen("test.txt", "r", stdin);
 67     scanf("%d%d", &n, &m);
 68     treeArray.clear(n+m);
 69     for (int i = 1; i <= n+m; ++i) fa[i] = i;
 70     for (int i = 1; i <= n; ++i) {
 71         scanf("%d", a+i);
 72         int t = get(a[i]); ++f[t];
 73         b[i] = treeArray.get(a[i]-1);
 74         treeArray.add(t, 1);
 75         fa[t] = t + 1;
 76     }
 77
 78     treeArray.clear(n+m);
 79     for (int i = 1; i <= n+m; ++i) {
 80         if (f[i] == 0) continue;
 81         treeArray.add(i, 1);
 82     }
 83
 84     memset(a, 0, sizeof(a));
 85     for (int i = n; i; --i) {
 86         int l = 1, r = n+m, mid, t;
 87         while (l <= r) {
 88             mid = (l+r)>>1;
 89             if ((t = treeArray.get(mid)) == b[i]+1 && f[mid] == 1) {
 90                 treeArray.add(mid, -1); f[mid] = 0;
 91                 a[mid] = i; break;
 92             } else if (t < b[i]+1) {
 93                 l = mid + 1;
 94             } else {
 95                 r = mid - 1;
 96             }
 97         }
 98     }
 99     for (int i = MAXN-1; i; --i) if (a[i]) {
100         printf("%d\n", i);
101         for (int j = 1; j <= i; ++j) printf("%d%c", a[j], j == i ? ‘\n‘ : ‘ ‘);
102         break;
103     }
104
105     return 0;
106 }

POJ 2131

时间: 2024-12-20 05:46:20

SYSU-5, POJ 2131, 树状数组+二分的相关文章

POJ 2892 Tunnel Warfare (树状数组+二分)

题目大意: 三个操作 D pos  将pos位置摧毁,让它和周围不相连. Q pos 问和pos 相连的有多少个村庄. R 修复最近摧毁的村庄. 思路分析: 树状数组记录这个区间有多少个1. 如果  [s-e] 有e-s+1个1 的话.那么这个区间是相连的. 这样的话,我们就可以用二分的办法求出与某个位置最大相连的数量. 还有这里二分 while(l<=r) { if(满足) { ans=mid; l=mid+1; } else r=mid-1; } #include <cstdio>

POJ 2828 Buy Tickets (线段树 or 树状数组+二分)

题目链接:http://poj.org/problem?id=2828 题意就是给你n个人,然后每个人按顺序插队,问你最终的顺序是怎么样的. 反过来做就很容易了,从最后一个人开始推,最后一个人位置很容易就确定了,那最后第二个人的位置也可以推(与最后一个人的位置无关)...依次就都可以确定所有的人了. 用前缀和的思想,要是这个人的位置确定了,那么就标记这个人位置的值为0,然后回溯更新,跟求逆序对个数的思想比较类似. 线段树: 1 #include <iostream> 2 #include &l

HDU 2852 KiKi&#39;s K-Number (树状数组 &amp;&amp; 二分)

题意:给出对容器的总操作次数n, 接下来是这n个操作.这里对于一个容器提供三种操作, 分别是插入.删除和查找.输入0  e表示插入e.输入1  e表示删除e,若元素不存在输出No Elment!.输入2  e  k表示查找比e大且第k大的数, 若不存在则输出Not Find! 分析:这里考虑树状数组做的原因是在第三个操作的时候, 只要我们记录了元素的总数, 那通过求和操作, 便能够高效地知道到底有多少个数比现在求和的这个数要大, 例如 tot - sum(3)就能知道整个集合里面比3大的数到底有

ZOJ 3635 树状数组+二分

这题那时怎么想就是想不出来--而且今晚没有多大状态,自己都晕了--一题没做出来-- baoge解释好久才懂--唉--线段树,树状数组用得还是不够熟啊-- WA了二发,才知道二分错了,二分好久不用,老是出错了现在-- #include<iostream> #include<cstring> #include<string> #include<cstdio> #define sca(a) scanf("%d",&a) #define

poj 2299 树状数组求逆序数+离散化

http://poj.org/problem?id=2299 最初做离散化的时候没太确定但是写完发现对的---因为后缀数组学的时候,,这种思维习惯了吧 1.初始化as[i]=i:对as数组按照num[]的大小间接排序 2.bs[as[i]]=i:现在bs数组就是num[]数组的离散化后的结果 3.注意,树状数组中lowbit(i)  i是不可以为0的,0&(-0)=0,死循环... #include <cstdio> #include <cstring> #include

HDU 5592 ZYB&#39;s Premutation(树状数组+二分)

题意:给一个排列的每个前缀区间的逆序对数,让还原 原序列. 思路:考虑逆序对的意思,对于k = f[i] - f[i -1],就表示在第i个位置前面有k个比当前位置大的数,那么也就是:除了i后面的数字之外,它是在剩下的数字当中第k+1大的. 知道这个之后,可以用树状数组来帮助找出剩下的数中第k大的数,刚开始我们可以让1-n中每个元素都标记为1,那么他们的前缀和就代表它是第几小.所以,我们可以对于他们的和来二分快速寻找第k大数.其实在树状数组里面是按照第(i-k)小来找的.找完之后要删除这个元素的

HDU 2852 KiKi&#39;s K-Number【 树状数组 二分 】

题意:给出m个操作,0:是增加一个数,add(x,1)1:是删除一个指定的数,这个是看sum(x) - sum(x-1)是否为0,为0的话则不存在,不为0的话,则add(x,-1)2:是查询比x大的数中第k大的数,先求出比x小的个数s,假设比x大的数中第k大的数为y,那么比y小的个数有s+k个二分y的值来找 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath&g

HDU 2852 KiKi&#39;s K-Number(树状数组+二分)

KiKi's K-Number Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2598    Accepted Submission(s): 1199 Problem Description For the k-th number, we all should be very familiar with it. Of course,t

HDU2852 KiKi&#39;s K-Number 树状数组+二分

这题就是给了你三种操作, 1:往容器中一个元素 x 2::把容器中的元素x删除 3:查询比 x大的第k个数 想法:添加元素跟删除元素  直接是以数本身为序号然后以 value值为1和-1即可,相当于计数,至于找比x第k个大的数,那就看看当前往后数k个数的第一个数是哪个就可以了,一开始直接找出来,然后往后暴力的扫了一遍,结果错了,没关系,反应很快,直接改了个二分查找,然后就过了,弄清楚如何建立这个树状数组即可 #include<iostream> #include<cstdio> #