把评分从小到大排序,在所有的评分中,二分出一个答案x,去验证x 是否合法;
dp[i] :i为最后一个数时,并且i>=x,前面需要添加的>=x的数个数;
如果dp[最后一个数]所需要添的>=x的数个数<=不知道位置的数的,那么合法;
dp[1-n]都是最初的状态;
如果i位置上的数<x,无论加多少个都不可能使它本身>=x,所以赋值为inf
如果i位置上的数>=x,它本身>=x,所以赋值为0;
如果i位置上的数未知,赋值为1;
然后就模拟,每次取队首前三个,用最小的两个数的和
如果两个数的和<inf说明有两个都是>=x的数,那么新的队尾一定也可以>=x;dp[++tail] = 两个数的和
如果两个数的和>=inf说明有两个都是<x的数,那么新的队尾一定也可以<x;dp[++tail] = inf(如果赋值成 两个数的和会加爆的)
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
int a[200000], b[200000],c[200000], n, m;
long long dp[600000];
int check(int x)
{
memset(dp,0,sizeof(dp));
int shu = 0;
for(int i = 1; i <= n - m; i++)
{
if(b[i] >= x) shu ++;
}
for(int i = 1; i <= n; i++)
{
if(a[i] == 0) dp[i] = 1;
else if(a[i] >= x) dp[i] = 0;
else dp[i] = 1e9;
}
int head = 1, tail = n;
while(head + 1< tail)
{
long long t = dp[head + 1] + dp[head] + dp[head + 2];
long long ha = max(dp[head + 1], dp[head]);
ha = max(dp[head + 2], ha);
if(t - ha > 1e9)dp[++tail] = 1e9;
else dp[++tail] = t - ha;
head += 3;
}
if(dp[tail] > shu) return 0;
return 1;
}
int main()
{
cin >> n >> m;
int cnt = 0;
for(int i = 1; i <= m; i++)
{
int d, v;
scanf("%d%d",&v,&d);
a[d] = v;
c[++cnt] = v;
}
for(int i = 1; i <= n - m; i++)
{
scanf("%d",&b[i]);
c[++cnt] = b[i];
}
sort(1+c,1+c+cnt);
int ans = 0,l = 1, r = cnt;
while(l <= r)
{
int mid = (l + r) / 2;
if(check(c[mid]) == 1)
{
l = mid + 1;
ans = c[mid];
}
else r = mid - 1;
}
cout << ans;
return 0;
}