H - Pavel's Party ( 权值线段树 + 思维 )
题目链接:https://vjudge.net/problem/Gym-100971H
题意:Pavel 将要举行一个聚会,他想确切地邀请k个人参加。他有n个朋友,并且他已经决定按什么顺序打电话和邀请他们,每个朋友会回复他两个值 l 和 r,代表如果这个聚会的人数在[ l, r ] 之间他就会参加。Pavel一旦集合了所需的人数,就会立刻开启聚会,不会给其余的朋友打电话。询问k为1~n时,分别,最少需要给前多少人打电话。如果给n个朋友全打完也凑不到人就输出-1.
Input
6 3 3 1 2 3 6 3 4 1 4 4 6
Output
2 5 4 6 -1 -1
样例解释:6个朋友,分别给出ai bi,
输出的2表示当Pavel准备组织1个人的聚会需要给前2个人打电话。
输出的5表示当Pavel准备组织2个人的聚会需要给前5个人打电话。
输出的4表示当Pavel准备组织3个人的聚会需要给前4个人打电话。
输出的6表示当Pavel准备组织4个人的聚会需要给前6个人打电话。
思路:我们做一颗权值线段树维护所有人的编号。当为1时,表示这个人可以来; 为0时,表示 这个人没发来。准备两个数组(里面存每个人的 l ,r,id),一个是左边界升序(a数组),一个是右边界升序(b数组)。
然后for循环从1到n枚举Pavel准备组织k个人。当枚举到k时,把a数组里 l 等于k的加入权值线段树。 把b数组里 r 等于k的从权值线段树里删除。在中间更新ans[ k ] = -1, 或者是线段树里第 k 个人的编号。
代码:
#include <bits/stdc++.h>
#define mid ((left+right)/2)
#define lson node*2,left,mid
#define rson node*2+1,mid+1,right
using namespace std;
struct node {
int l,r,id;
}a[200005],b[200005];
int n;
int tree[200005*4]; /// 维护人的编号
int ans[200005];
int rule( node a, node b ) {return a.l<b.l;}
int rule2( node a, node b ) {return a.r<b.r;}
void update( int node, int left, int right, int id, int val )
{
if ( left==right ) {
tree[node] += val;
return ;
}
if ( id<=mid ) update(lson,id,val);
if ( id>mid ) update(rson,id,val);
tree[node] = tree[node*2] + tree[node*2+1];
}
int query( int node, int left, int right, int k ) /// 返回第k个人的编号
{
if ( left==right ) return left;
if ( tree[node*2]>=k ) return query(lson,k);
else return query(rson,k-tree[node*2]);
}
int main()
{
cin >> n;
for ( int i=1; i<=n; i++ ) {
scanf("%d %d",&a[i].l,&a[i].r);
a[i].id = i;
b[i] = a[i];
}
sort(a+1,a+1+n,rule); /// 加入的人
sort(b+1,b+1+n,rule2); /// 删除的人
memset(tree,0,sizeof(tree));
int posa = 1, posb = 1;
for ( int k=1; k<=n; k++ ) {
while ( posa<=n && a[posa].l==k ) { /// 插入
update(1,1,n,a[posa].id,1);
posa++;
}
if ( tree[1]<k ) ans[k] = -1;
else ans[k] = query(1,1,n,k); /// 更新答案
while ( posb<=n && b[posb].r==k ) { /// 删除
update(1,1,n,b[posb].id,-1);
posb++;
}
}
for ( int i=1; i<=n; i++ ) cout << ans[i] << " ";
return 0;
}