题意:在一个队列中,你知道一个人在他左边或者右边比他高的人的个数,求字典序最小的答案
思路:先将人按 矮-->高 排序,然后算出在每个人前面需要预留的位置。树状数组(也可以线段树)解决时,先二分查找合适的位置,如果合理则标记,每次查找时 枚举的人数 - 前面已占的人数 = 可预留位置
判断分别在它左边 或者 右边时可能的最小值进行比较。
预留的位置①:pnode[i].num(即比他高的人数,全在左)
②:n - ( pnode[i].num + i) (当比他高,和前面加入的全在右边时) //为了字典序最小
/* 当时是一点头绪都没有- -,没想到是可以用线段树和二维数组,果然太年轻
#include <cstdio> #include <cstring> #include <algorithm> #define MAXN 100010 using namespace std; const int N = 1e5 + 5; int n,m; int tree[N]; struct node { int id,num,position; }pnode[N]; bool cmp(node a,node b) { return a.id < b.id; } bool cmp2(node a,node b) { return a.position < b.position; } void add(int x , int dx) { while(x < N) { tree[x] += dx ; x += x&(-x) ; } } int query(int x) { int sum = 0 ; while(x) { sum += tree[x] ; x -= x&(-x) ; } return sum ; } int fin(int l,int r,int x) { if(x < 0) return -1; while(l <= r) { int mid = (l + r)/2; int tt= mid - query(mid); //找出前面有多少空位 if(tt < x) l =mid + 1; else r = mid - 1; } return l; } void ini() { for(int i = 0;i <= n;i++) tree[i] = 0; } int main() { int t,cas = 1; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i = 1;i <= n;i++) scanf("%d%d",&pnode[i].id,&pnode[i].num); sort(pnode+1,pnode+1+n,cmp); ini(); int flag = 0; for(int i = 1;i <= n;i++) { int p = n - (i - 1) ; //求还剩下多少位置 int tp = pnode[i].num; if(p <= tp) //判断剩下的能否预留合理位置 flag = true; int x1 = fin(1,n,pnode[i].num + 1); int x2 = fin(1,n,n -pnode[i].num-i + 1); pnode[i].position = min(x1,x2); add(pnode[i].position,1); } printf("Case #%d: ", cas++); if(flag ) { printf("impossible "); continue; } sort(pnode+1,pnode+n+1,cmp2); printf("%d", pnode[1].id); for (int i = 2; i <= n; i++) printf(" %d", pnode[i].id); printf(" "); } return 0; }
ps.如果没有东西值得你为之努力,那你和一条咸鱼有什么区别?