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;
}