传送门:LibreOJ-6062Pair
给出一个长度为 n n 的数列 {ai} { a i } 和一个长度为 m m 的数列 {bi} { b i } ,求 {ai} { a i } 有多少个长度为 m m 的连续子数列能与 {bi} { b i } 匹配。
两个数列可以匹配,当且仅当存在一种方案,使两个数列中的数可以两两配对,两个数可以配对当且仅当它们的和不小于 h h 。
对于 10% 10\% 10% 的数据, 1≤m≤n≤10 1 ≤ m ≤ n ≤ 10 ;
对于 40% 40\% 40% 的数据, 1≤m≤n≤1000 1 ≤ m ≤ n ≤ 1000 ;
对于 100% 100\% 100% 的数据, 1≤m≤n≤150000 1 ≤ m ≤ n ≤ 150000 ;
对于 100% 100\% 100% 的数据, 1≤ai,bi,h≤109 1 ≤ a i , b i , h ≤ 10 9 。
霍尔定理(皮一皮,自己上网搜)一秒出题解。
我们可以先对b按升序排序。所以每个 ai a i 满足的b的区间都是b的一个后缀,不然就没有。
然后我们每次加入一个数时,把符合条件(即后缀)中的所有b上的值+1,(姑且设一个数组为 sum[i] s u m [ i ] )然后判断一下对于每个 bi b i ,是否都满足 sum[i]≤i s u m [ i ] ≤ i (由霍尔定理)。
然而本蒟蒻始终有一种不详的预感:然而我们还是要扫一遍,一个一个加入线段树中又一个一个删去。这样写会T。
果真本蒟蒻只拿了40分。
然而我们维护的并不是 sum s u m ,而是每个位置上的 sum[i]−i s u m [ i ] − i 。每次 O(1) O ( 1 ) 判断最小的这个值是否大于0就好了。
orzzzz
#include
#include
#include
#include
#define IT int
#define rg register
using namespace std;
const IT N=150050;
IT n,m,b[N],H,in[N];
IT ans,add[N<<2],mn[N<<2];
char nc()
{
static char buf[100000],*L=buf,*R=buf;
if(L==R){R=(L=buf)+fread(buf,1,1e5,stdin);}
if(L==R)return EOF;else return *L++;
}
inline int read()
{
int num=0;char ch=nc();
while(ch<'0' || ch>'9') ch=nc();
while(ch>='0' && ch<='9') {num=(num<<3)+(num<<1)+(ch^48);ch=nc();}
return num;
}
inline IT min(IT x,IT y)
{
return x>y? y:x;
}
inline void pushup(IT k)
{
mn[k]=min(mn[k<<1],mn[k<<1|1]);
}
inline void pushdown(IT k,IT l,IT r)
{
if(add[k]!=0){
IT mid=(l+r)>>1;
add[k<<1]+=add[k];
mn[k<<1]+=add[k];
add[k<<1|1]+=add[k];
mn[k<<1|1]+=add[k];
add[k]=0;
}
}
inline IT find(IT x)
{
IT l=1,r=m;
while(l<=r){
IT mid=(l+r)>>1;
if(b[mid]>=x) r=mid-1;
else l=mid+1;
}
return l;
}
inline void build(IT k,IT l,IT r)
{
if(l==r){
mn[k]=-l;return;
}
IT mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k);
}
inline void ad(IT k,IT l,IT r,IT L,IT val)
{
pushdown(k,l,r);
if(l>=L){
add[k]+=val;
mn[k]+=val;
return;
}
int mid=(l+r)>>1;
if(L<=mid) ad(k<<1,l,mid,L,val);
ad(k<<1|1,mid+1,r,L,val);
pushup(k);
}
inline int get(IT k,IT l,IT r)
{
pushdown(k,l,r);
return mn[k];
}
int main(){
n=read();m=read();H=read();
for(rg IT i=1;i<=m;i++){
b[i]=read();
}
sort(b+1,b+m+1);
for(rg IT i=1;i<=n;i++){
in[i]=find(H-read());
}
build(1,1,m);
for(rg IT i=1;i<=m;i++){
if(in[i]>m) continue;
ad(1,1,m,in[i],1);
}
if(get(1,1,m)>=0) ans++;
for(rg int i=m+1;i<=n;i++){
if(in[i-m]<=m)
ad(1,1,m,in[i-m],-1);
if(in[i]<=m)
ad(1,1,m,in[i],1);
if(get(1,1,m)>=0) ans++;
}
printf("%d\n",ans);
return 0;
}
ps:请无视掉本蒟蒻开始为了卡常(然而还是T了)加的ncbuf