题目链接:https://codeforces.com/problemset/problem/567/D
题意:
在一个 $1 \times n$ 的网格上,初始摆放着 $k$ 只船,每只船的长度均为 $a$ 个格子,已知所有船之间均不重叠、不触碰。
现在Bob每次询问Alice第 $i$ 个格子上是否存在船,Alice每次都会说不存在,求在第几次询问时,可以确定Alice撒谎了。
题解:
对于某次询问一个位置 $x$ 是否有船,假设其属于某个最小的区间 $(l,r)$,其中 $l,r$ 分别是曾经询问过的位置。我们用树状数组配合二分 $O(\log^2 n)$ 寻找出 $l,r$。
那么,可以计算出,$(l,r)$ 区间曾经最多能停放多少船只,而现在变成了两个区间 $(l,x)$ 和 $(x,r)$ 后,又能停放多少船只。
这样一来,最开始我们计算出整个区域 $(0,n+1)$ 最多放多少船只 $cur$,进而对每次计算都能计算出减少了多少船只,即 $cur$ 会减去一个数,直到某一次询问,使得 $cur<k$,即代表Alice撒谎了。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int n,k,a,m;
struct _BIT
{
int N,C[maxn];
#define lowbit(x) (x&(-x))
void init(int n) //初始化共有n个点
{
N=n;
for(int i=1;i<=N;i++) C[i]=0;
}
void add(int pos,int val) //在pos点加上val
{
while(pos<=N)
{
C[pos]+=val;
pos+=lowbit(pos);
}
}
int ask(int pos) //查询1~pos点的和
{
int ret=0;
while(pos>0)
{
ret+=C[pos];
pos-=lowbit(pos);
}
return ret;
}
}BIT;
int lower(int x)
{
int l=1, r=BIT.N;
while(l<r)
{
int mid=(l+r)>>1;
if(BIT.ask(mid)<x) l=mid+1;
else r=mid;
}
return l;
}
int upper(int x)
{
int l=1, r=BIT.N;
while(l<r)
{
int mid=(l+r)>>1;
if(BIT.ask(mid)<=x) l=mid+1;
else r=mid;
}
return l;
}
int main()
{
cin>>n>>k>>a>>m;
BIT.init(n+2);
BIT.add(1,1), BIT.add(n+2,1);
int cur=(n+1)/(a+1);
for(int i=1,x;i<=m;i++)
{
scanf("%d",&x), x++;
int tp=BIT.ask(x);
int l=lower(tp), r=upper(tp);
int old=(r-l)/(a+1);
int now=(r-x)/(a+1)+(x-l)/(a+1);
cur-=old-now;
if(cur<k)
{
printf("%d\n",i);
return 0;
}
BIT.add(x,1);
}
printf("-1\n");
}