2 1 1 10 4 8 3 4 2 0 5 7 1 6 0
Impossible -8 -2 -10 -7 8 2 10 -4 7 -1 4 -3 -5 -9 1 3 5 9 -6 6
题目大意:有n个公司编号1-n,每个公司有相应的一个子公司,编号i的公司的子公司为-i。每个公司都有一些员工,人数未知,但是每个公司员工人数大小有如下关系:
1。每个公司的人数都大于其子公司人数;
2。如果第i个公司人数>第j个公司人数,当且仅当-i公司的人数> -j公司的人数。
现在给n个数,c[i]表示标号在[i + 1,n]和[-(i - 1) ,-1]的区间中人数比第i个公司人数多的个数。
题目分析:线段树。比赛的时候还真yy到线段树了,可惜没有坚持继续往下想。
题目给的c[i]其实就是第i个公司在上述区间的rank值,处理这种情况的话,一般先找最大的。假设最大的也就是c[i] = 0的那个i,一定是最大的,那么我们确定i之后就可以踢掉i了,因为下面要做的跟第i个公司没关系了,那么踢掉第i个公司的话,会对其他公司有影响,第i个公司一定在[1,i - 1]中所有公司的右边,那么第i个公司也就是[1,i - 1]所有公司的rank的组成部分,踢掉i后,对于区间[1,i - 1]每个点的rank 也是要减去1的。踢掉i后再看是否有rank 0的点,有的话就是最大的,跟i相同处理,如果剩下的min(rank)<0,则可以判断非法。如果剩下的min(rank)>0,那么说明剩下的编号[1-n]中就没有最大的了,所以最大的一定在[-n,-1]中,根据第二个条件,要把最先踢掉的i的子公司-i作为最大的,并踢之。在踢编号[-n,-1]的点时要注意,标号为负的点影响的是[i + 1,n]的点,所以要把区间[i + 1,n]的rank都减1。
所以我们要做的就是快速更新某个区间的值以及快速查找当前区间最小值。线段树再合适不过了。
维护2个队列,que1和que2,第一个存答案,就是我们要踢掉的当前rank为0的点。第二个存编号[-n,-1]的点,每踢掉一个[1,n]的点,就把相应的子公司的编号加入que2,下次优先踢这个点。
还要注意的是如果当前有多个rank 0的点,要找位置小的那个(想一想,为什么)。
今天做这题也花了好久,其实结论昨晚就分析出来了,原因是处理的时候,我是往上加的,思路跟分析的一样,区别就是减法做成了加法,这样找当前rank最大的点的时候就不好找,也就是要找当前区间中累加和c[i]最接近的点,还要保证位置尽量小,这样线段树做起来非常麻烦,导致debug了半天。这种sb错误实在不该犯。。。
详情请见代码:
#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N = 100005; const int INF = 0x3f3f3f3f; struct node { int lazy,mn; }tree[N<<2]; int n; int pos; bool flag; int que1[N + N],que2[N + N]; int h1,h2,t1,t2; int Min(int a,int b) { return a > b ? b:a; } void build(int num,int s,int e) { tree[num].lazy = tree[num].mn = 0; if(s == e) { scanf("%d",&tree[num].mn); return; } int mid = (s + e)>>1; build(num<<1,s,mid); build(num<<1|1,mid + 1,e); tree[num].mn = Min(tree[num<<1].mn,tree[num<<1|1].mn); } void insert(int num,int s,int e,int l,int r) { if(l > r) return; if(s == l && e == r) { tree[num].lazy --; return; } if(tree[num].lazy != 0) { tree[num<<1].lazy += tree[num].lazy; tree[num<<1|1].lazy += tree[num].lazy; tree[num].mn += tree[num].lazy; tree[num].lazy = 0; } int mid = (s + e)>>1; if(r <= mid) insert(num<<1,s,mid,l,r); else { if(l > mid) insert(num<<1|1,mid + 1,e,l,r); else { insert(num<<1,s,mid,l,mid); insert(num<<1|1,mid + 1,e,mid + 1,r); } } tree[num].mn = Min(tree[num<<1].lazy + tree[num<<1].mn,tree[num<<1|1].lazy + tree[num<<1|1].mn); } void query(int num,int s,int e) { if(s == e) { pos = s; if(tree[num].mn + tree[num].lazy == 0) tree[num].mn = INF; return ; } if(tree[num].lazy != 0) { tree[num<<1].lazy += tree[num].lazy; tree[num<<1|1].lazy += tree[num].lazy; tree[num].mn += tree[num].lazy; tree[num].lazy = 0; } int mid = (s + e)>>1; if(tree[num<<1].mn + tree[num<<1].lazy <= tree[num<<1|1].lazy + tree[num<<1|1].mn) query(num<<1,s,mid); else query(num<<1|1,mid + 1,e); tree[num].mn = Min(tree[num<<1].lazy + tree[num<<1].mn,tree[num<<1|1].lazy + tree[num<<1|1].mn); } void solve() { while(t1 - h1 != n + n) { while(tree[1].mn == 0) { query(1,1,n); que1[t1 ++] = pos; que2[t2 ++] = -pos; insert(1,1,n,1,pos - 1); } if(tree[1].mn < 0)//当前最小rank为负,不科学,无解 { flag = false; return; } while(tree[1].mn > 0) { if(h2 >= t2) { if(tree[1].mn > N)//找完了 return; flag = false;//没找完,但是que2中没有元素了,也就是说 return;//当前rank没有为0的,不科学,无解 } que1[t1 ++] = que2[h2]; insert(1,1,n,1 - que2[h2 ++],n); } } } int main() { int i,j; while(scanf("%d",&n),n) { build(1,1,n); h1 = h2 = t1 = t2 = 1; flag = true; solve(); if(flag == true) { for(i = t1 - 1;i >= h1;i --) printf("%d ",que1[i]); putchar(10); } else puts("Impossible"); } return 0; } //234MS 3468K /* 2 1 1 10 4 8 3 4 2 0 5 7 1 6 5 0 1 2 3 4 5 0 2 2 1 1 5 0 1 2 1 2 0 */