传送门:HDU5493
题意:给出n个人的身高和每个人前面或者后面有多少人比他高(不知道是前面还是后面),问能否构造出一个合法的序列。
思路1:将所有人按身高从小到大排序,然后一个个取出来插入线段树,插入线段树的时候要保证前面留出足够的空来给比他高的人,又因为要字典序最小,那么我们插入的位置就是要min(ki, n - i - ki - 1) + 1.
代码:
#include<bits/stdc++.h>
#define ll long long
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
using namespace std;
const int MAXN = 100010;
struct node{
int h, k;
bool operator < (node a) const{
return h < a.h;
}
}p[MAXN];
int tree[MAXN << 2], ans[MAXN];
void build(int l, int r, int rt)
{
if(l == r){
tree[rt] = 1;
return ;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
void update(int x, int val, int l, int r, int rt)
{
if(l == r)
{
ans[l] = val;
tree[rt] = 0;
return ;
}
int mid = (l + r) >> 1;
if(x >= tree[rt << 1]) update(x - tree[rt << 1], val, rson);
else update(x, val, lson);
tree[rt] = tree[rt << 1] + tree[rt << 1 | 1];
}
int main()
{
int T, n, kase = 1;
cin >> T;
while(T--)
{
scanf("%d", &n);
build(1, n, 1);
for(int i = 0; i < n; i++)
scanf("%d %d", &p[i].h, &p[i].k);
sort(p, p + n);
bool ok = 1;
for(int i = 0; i < n; i++)
{
if(n - i - p[i].k <= 0){
ok = 0;
break;
}
update(min(p[i].k, n - i - p[i].k - 1), p[i].h, 1, n, 1);//前面留min()个空位置
}
printf("Case #%d: ", kase++);
if(!ok){
cout << "impossible\n";
continue;
}
for(int i = 1; i <= n; i++)
printf("%d%c", ans[i], " \n"[i == n]);
}
}
思路2:将所有人身高按从大到小排序,每次取出一个人来插入Treap中,Treap维护已经构造出来的序列,那么新加的人插入的位置就是min(ki, x - ki) + 1。
Treap学习及模板:点击打开链接
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
struct Treap
{
int size;
int key,fix;
Treap *ch[2];
Treap(int key)
{
size=1;
fix=rand();
this->key=key;
ch[0]=ch[1]=NULL;
}
int compare(int x) const
{
if(x==key) return -1;
return x<key? 0:1;
}
void Maintain()
{
size=1;
if(ch[0]!=NULL) size+=ch[0]->size;
if(ch[1]!=NULL) size+=ch[1]->size;
}
};
void Rotate(Treap* &t,int d)
{
Treap *k=t->ch[d^1];
t->ch[d^1]=k->ch[d];
k->ch[d]=t;
t->Maintain();
k->Maintain();
t=k;
}
void Insert(Treap* &t,int pos, int val)
{
if(t==NULL) t=new Treap(val);
else
{
int d;
if(pos <= (t->ch[0] ? t->ch[0]->size : 0)) d = 0;
else {
d = 1; pos = pos - (t->ch[0] ? t->ch[0]->size : 0) - 1;//注意这里,每个子树的下标都是从零开始的,因此进入右子树时下标要-1
}
Insert(t->ch[d], pos, val);
if(t->ch[d]->fix > t->fix)
Rotate(t, d^1);
}
t->Maintain();
}
void Print(Treap *t)
{
if(t==NULL) return;
Print(t->ch[0]);
printf(" %d", t->key);
Print(t->ch[1]);
delete t;//本题要注意在这里释放空间
}
struct node{
int h, k;
bool operator < (node a) const{
return h > a.h;
}
}p[MAXN];
int main()
{
int T, n, kase = 1;
cin >> T;
while(T--)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%d %d", &p[i].h, &p[i].k);
sort(p, p + n);
Treap *root = NULL;
bool ok = 1;
for(int i = 0; i < n; i++)
{
int pos = min(p[i].k, i - p[i].k);
if(pos < 0){
ok = 0; break;
}
Insert(root, pos, p[i].h);
}
printf("Case #%d:", kase++);
if(!ok)puts(" impossible");
else Print(root), puts("");
}
return 0;
}
Treap效率稍微比线段树差一点,可能是要申请和释放空间的缘故。