题意:人们一个一个的来排队并插队,按人到来的顺序给出每个人插队的位置(插在第几个人后面),并告知每个人的id号,输出最终队伍的情况。
思路:按照hint给的思路显然是n2复杂度,对于200000个点来说大了点。
1、按照与2182同样的思路可以用树状数组来做。只不过这次二分是求第i个人应该最终排在哪个位置。(树状数组版本2中,tree[i]存放的是1~i的空余位置,具体思路见代码注释)
2、线段树也能做,记录区间目前还剩的空位数量,每一次插入的时候,如果该节点左儿子的空余数量>x,那么只要在左儿子找就可以了;否则要在右儿子中找,此时x改为x-左儿子num。
树状数组代码:
#include <stdio.h> #include <string.h> #define N 200005 int n; int a[N],res[N],s[N],tree[N]; int lowbit(x){ return x&(-x); } void add(int x){ int i; for(i = x;i<=n;i+=lowbit(i)) tree[i]++; } int sum(int x){ int i,ans=0; for(i = x;i>=1;i-=lowbit(i)) ans += tree[i]; return ans; } int bisearch(int x){ int low,high,mid; low = 1,high = n; while(low < high){ mid = (low+high)>>1; if(x + sum(mid) >= mid) low = mid+1; else high = mid; } return low; } int main(){ freopen("a.txt","r",stdin); while(scanf("%d",&n)!=EOF){ int i,j; memset(tree,0,sizeof(tree)); for(i = 1;i<=n;i++) scanf("%d %d",&a[i],&s[i]); for(i = n;i>=1;i--){ j = bisearch(a[i]); res[j] = s[i]; add(j); } for(i = 1;i<=n;i++) printf("%d ",res[i]); putchar('\n'); } return 0; }
树状数组代码,版本2:
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #include <cstdlib> using namespace std; #define clc(s,t) memset(s,t,sizeof(s)) #define INF 0x3fffffff #define N 200005 int tree[N],a[N],s[N],res[N]; int n; int lowbit(int x){ return x&(-x); } void add(int i,int x){ for(int j = i;j<=n;j+=lowbit(j)) tree[j] += x; } int sum(int i){ int res = 0; for(int j = i;j>=1;j-=lowbit(j)) res += tree[j]; return res; } int search(int d,int x){//相当于要插入的位置是d右侧的第x个空位上 int high,mid,low,tmp; low = d; high = n; while(low < high){ mid = (low+high)>>1; tmp = sum(mid) - sum(d); if(tmp < x)//二分时要注意:tmp==x是不能直接输出mid,因为mid处可能已经填过东西,所有要找到最靠左的tmp==x处 low = mid+1; else high = mid; } return low; } int main(){ while(scanf("%d",&n) != EOF){ int i,j; clc(tree,0); for(i = 1;i<=n;i++){ scanf("%d %d",&a[i],&s[i]); add(i,1); } for(i = n;i>=1;i--){ j = search(a[i]+1,a[i]+1-sum(a[i]+1));//a[i]+1-sum(a[i]+1)表示要插入的位置左侧已经插入了多少个人 add(j,-1); res[j] = s[i]; } for(i = 1;i<=n;i++) printf("%d ",res[i]); putchar('\n'); } return 0; }
线段树代码:
#include <stdio.h> #include <string.h> #define N 200005 struct node{ int left,right; int num; }p[N<<2]; int a[N],s[N],res[N],n; void create(int id,int left,int right){ int mid; p[id].left = left; p[id].right = right; if(left == right){ p[id].num = 1; return; } mid = (left+right)>>1; create(id<<1,left,mid); create((id<<1)+1,mid+1,right); p[id].num = p[id<<1].num + p[(id<<1)+1].num; } int query(int id,int x){ p[id].num--; if(p[id].left == p[id].right) return p[id].left; if(p[id<<1].num <= x) query((id<<1)+1,x-p[id<<1].num); else query(id<<1,x); } int main(){ freopen("a.txt","r",stdin); while(scanf("%d",&n)!=EOF){ int i,j; for(i = 1;i<=n;i++) scanf("%d %d",&a[i],&s[i]); create(1,1,n); for(i = n;i>=1;i--){ j = query(1,a[i]); res[j] = s[i]; } for(i = 1;i<=n;i++) printf("%d ",res[i]); putchar('\n'); } return 0; }