http://acm.hdu.edu.cn/showproblem.php?pid=3564
(1)和今天刚做过的一道题十分相似(夜间插队问题hdu2828),从后面起算,因为最后面的数的位置是固定的,而其他数的位置被其后面的树影响着。
(2)一个易错点,在insert()函数中:
if(p<=sum[rt<<1]) insert(p, val, lson); else insert(p-sum[rt<<1], val, rson);
注意每个字段的含义(sum[]数组是空位的个数,p是需要预留的空位数),很多时候不加思考,就写错成了:
if(p<=m) insert(p, val, lson); else insert(p, val, rson);
这是完全没有注意变量的具体含义,机械式地敲代码的结果(为此,我WA了两次。。)
(3)二分查找,快速找到相应的位置。
(4)关于pos[i]数组的具体含义。该数组是在insert()函数下算得的,i 既是数的编号,即第几个插入,又是对应的数的值,pos[i]是所有点都插入后各个
数字对应的下标(最终位置)。
(5)注意到后面插入的数的值总是比前面插入的数大,所以二分查找的查找根据是它的最终位置,而不是自己本身的值(这点很难把握),即:
ans[start]=pos[i];
而不是:
ans[start]=i;
更多细节参见代码。
具体代码:
View Code
#include<stdio.h> #define lson l, m, rt<<1 #define rson m+1, r, rt<<1|1 const int maxn=101000; int t, n; int data[maxn], pos[maxn]; int sum[maxn<<2]; int tot, ans[maxn]; void build(int l, int r, int rt) { sum[rt]=r-l+1; if(l==r) return ; int m=l+r>>1; build(lson); build(rson); } void insert(int p, int val, int l, int r, int rt) { if(l==r) { sum[rt]=0; pos[val]=l; return ; } int m=l+r>>1; if(p<=sum[rt<<1]) insert(p, val, lson); else insert(p-sum[rt<<1], val, rson); sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } int main() { while(scanf("%d", &t)!=EOF) { int cas=0; while(t--) { printf("Case #%d:\n", ++cas); scanf("%d", &n); build(0, n, 1); for(int i=0;i<n;i++) scanf("%d", &data[i]); for(int i=n-1;i>=0;i--) { insert(data[i]+1, i+1, 0, n, 1); } tot=0; for(int i=1;i<=n;i++) { if(tot==0||pos[i]>ans[tot]) { ans[++tot]=pos[i]; } else { int start=1, tail=tot, mid; while(start<=tail) { mid=start+tail>>1; if(pos[i]<=ans[mid]) tail=mid-1; else start=mid+1; } ans[start]=pos[i]; } printf("%d\n", tot); } printf("\n"); } } return 0; }
