[??记录]AT2372 [AGC013F] Two Faced Cards
command_block
2021-04-29 15:58:38
**题意** : 给出 $n$ 个二元组,记为集合 $X$。和 $n+1$ 个整数,记为集合 $Y$。
有 $q$ 个**相互独立**的操作。
每次会向 $X$ 中加入一个二元组,然后你需要:
1. 对 $X$ 中每一个二元组选定一个元素作为这个二元组的值,产生新集合 $X'$
2. 将 $X',Y$ 中元素两两匹配(此时 $|X'|=|Y|=n+1$)。$u\in X'$ 能匹配 $v\in Y$ 当且仅当 $u\leq v$。
3. 如果匹配成功,获得的分数等于:第 1 步中,取第一个数作为值的二元组数量。
对于每个操作,求最大匹配分数,或指出无解。
$n,q\leq 10^5$ ,时限$\texttt{2s}$。
------------
首先将所有数关于 $Y$ 离散化,这样值域就变为 $1\sim n+1$。
先不考虑操作。若给定 $X,Y$ ,如何求出最大分数。
对于二元组 $(x_1,x_2)$ ,若 $x_1\leq x_2$ ,则必选 $x_1$ 。
对于剩下的二元组,按照 $x_1$ 排序,不难发现最优方案一定翻一个后缀,逐个判定即可。
- 如何判定序列 $\{a_{1\sim n}\}$ 在任意排列后能否满足 $a_i\leq b_i$ ?
利用辅助数组 $o$ ,对于 $a_i$ ,将 $o[1\sim a]$ 减一。
对于 $b_i$ ,将 $o[1\sim b_i]$ 加一。
若 $o$ 全部非负,则存在合法方案。
- 于是,若将一个 $x_1<x_2$ 的 $(x_1,x_2)$ 翻面,则相当于将 $o(x_2,x_1]$ 加一。
现在考虑操作。
对于一个新加入的二元组 $(x_1,x_2)$ ,可以选择 $x_1$ 或者 $x_2$ ,影响是将 $o$ 的一个前缀 $-1$。
- 我们将问题转化为了 :
给出 $o$ 数组,和若干个区间。
每次再(独立地)进行一次前缀减,问至少选多少个区间加一可以使得整个数组非负。或指出无解。
设这次单独的前缀减为 $[1,t]$ ,对于每个 $t$ 分别求出答案。
先求 $t=0$ 的答案。
从后往前查看 $o$ 中的元素,若遇到 $<0$ 的,则选取一个右端点大于等于它,且左端点尽量小的区间。这可以用堆来实现。
然后从小到大依次求 $t=1\sim n+1$ 的答案。
当 $t$ 增大时,$o[t]$ 会减一,若这之后 $o[t]<0$ ,则选择一个左端点小于等于它,且右端点尽量大的区间。这也用堆实现。
- 正确性理解 : 容易发现上述贪心得到的方案中,任意去掉一个区间,都会导致不合法。
区间加可以用差分实现。
复杂度为 $O(n\log n)$。
```cpp
#include<algorithm>
#include<cstdio>
#include<vector>
#include<queue>
#define Pr pair<int,int>
#define fir first
#define sec second
#define mp make_pair
#define pb push_back
#define MaxN 100500
using namespace std;
const int INF=1000000000;
int n,m,qt,x1[MaxN],x2[MaxN],y[MaxN],
s[MaxN],s2[MaxN],o[MaxN],ans[MaxN];
struct Data{int l,r;bool vis;}b[MaxN];
vector<int> gl[MaxN],gr[MaxN];
priority_queue<Pr> q;
int main()
{
scanf("%d",&n);n++;
for (int i=1;i<n;i++)
scanf("%d%d",&x1[i],&x2[i]);
for (int i=1;i<=n;i++){
scanf("%d",&y[i]);
o[i]=n-i+1;
}
sort(y+1,y+n+1);
for (int i=1;i<n;i++){
x1[i]=lower_bound(y+1,y+n+1,x1[i])-y;
x2[i]=lower_bound(y+1,y+n+1,x2[i])-y;
s[x1[i]]--;
if (x1[i]>x2[i])
b[++m]=(Data){x2[i]+1,x1[i]};
}
for (int i=n+1;i;i--)o[i]+=(s[i]+=s[i+1]);
for (int i=1;i<=m;i++){
gl[b[i].l].pb(i);
gr[b[i].r].pb(i);
}
bool fl=0;int sav=0;
for (int i=1;i<=n+1;i++)s[i]=0;
for (int i=n+1;i;i--){
s[i]+=s[i+1];
for (int j=0;j<gr[i].size();j++)
q.push(mp(-b[gr[i][j]].l,gr[i][j]));
while (o[i]+s[i]<0){
if (q.empty()){fl=1;break;}
int p=q.top().sec;q.pop();
b[p].vis=1;sav++;
s[i]++;s[b[p].l-1]--;
s2[b[p].r]++;s2[i]--;
}o[i]+=s[i];
}
for (int i=n+1;i;i--)o[i]+=(s2[i]+=s2[i+1]);
for (int i=1;i<=n+1;i++)s[i]=0;
while(!q.empty())q.pop();
for (int i=1;i<=n+1;i++){
s[i]+=s[i-1];
for (int j=0;j<gl[i].size();j++)
q.push(mp(b[gl[i][j]].r,gl[i][j]));
while (o[i]+s[i]<=0){
if (q.empty()){fl=1;break;}
int p=q.top().sec;q.pop();
if (b[p].vis)continue;
b[p].vis=1;sav++;
s[i]++;s[b[p].r+1]--;
}o[i]+=s[i];
ans[i]=fl ? INF : sav;
}ans[n+1]=INF;
scanf("%d",&qt);
for (int i=1,tx,ty;i<=qt;i++){
scanf("%d%d",&tx,&ty);
tx=lower_bound(y+1,y+n+1,tx)-y;
ty=lower_bound(y+1,y+n+1,ty)-y;
printf("%d\n",max(-1,n-min(ans[tx],ans[ty]+1)));
}return 0;
}
```