LINK

题意

给定数组 a a a,求分成 A , B A,B A,B集合的方案数。

A A A集合任意两个元素差值大于等于 x x x, B B B集合任意两个元素插值大于等于 y y y


先把 a a a数组排序,考虑状态设计

显然需要知道当前 A , B A,B A,B集合最大的元素是谁

一个单纯的想法是 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示考虑到第 i i i个数, A A A集合最大是 a j a_j aj, B B B集合最大是 b k b_k bk

发现 j , k j,k j,k必定有一个是 i i i,所以我们的状态只需要保存另一个索引即可

f [ i ] [ j ] [ 0 / 1 ] f[i][j][0/1] f[i][j][0/1]表示当前第 A / B A/B A/B集合最大的是 a i a_i ai, B / A B/A B/A集合最大的是 a j a_j aj

为了方便观察,我们令 f a [ i ] [ j ] fa[i][j] fa[i][j]表示 A A A集合最大是 a i a_i ai, f b [ i ] [ j ] fb[i][j] fb[i][j]表示 B B B集合最大是 a i a_i ai

如果上一个元素是属于 A A A集合的,那么

f a [ i ] [ j ] + = f a [ i − 1 ] [ j ]    ( j ∈ [ 0 , i − 2 ] & & a i − a i − 1 > = x ) fa[i][j]+=fa[i-1][j]\ \ (j\in[0,i-2]\&\&a_i-a_{i-1}>=x) fa[i][j]+=fa[i1][j]  (j[0,i2]&&aiai1>=x)

如果上一个元素是属于 B B B集合的,那么

f a [ i ] [ i − 1 ] + = ∑ j ∈ [ 0 , i − 2 ] & & a i − a j > = x f b [ i − 1 ] [ j ] fa[i][i-1]+=\sum\limits_{j\in[0,i-2]\&\&a_i-a_j>=x}fb[i-1][j] fa[i][i1]+=j[0,i2]&&aiaj>=xfb[i1][j]

对于 f b fb fb的转移同理

考虑把 f a , f b fa,fb fa,fb放在线段树上转移。

观察转移方程一,若当前 a i − a i − 1 < x a_i-a_{i-1}<x aiai1<x只需要把 [ 0 , i − 2 ] [0,i-2] [0,i2]位置置零即可,否则不用改变

观察转移方程二,我们可以二分出一个满足条件的最大 j j j,这样就是一个区间求和

都可以用线段树维护,整体复杂度 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

然而,发现操作一的零一定是从 0 , 1 , 2 , 3... 0,1,2,3... 0,1,2,3...这样顺序赋值过来的

操作二满足条件的最大 j j j一定是单调递增的

那么其实就是个小模拟,可以在 O ( n ) O(n) O(n)的时间内转移

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6+10;
const int mod = 1e9+7;
int n,x,y,a[maxn],fa[maxn],fb[maxn];
int azero,bzero,prea[maxn],preb[maxn];
int ida,idb;
int get(int l,int r,int pre[])
{
    if( r<l )   return 0ll;
    if( l==0 )  return pre[r];
    return ( pre[r]-pre[l-1] )%mod;
}
signed main()
{
    scanf("%lld%lld%lld",&n,&x,&y);
    for(int i=1;i<=n;i++)    scanf("%lld",&a[i] );
    sort( a+1,a+1+n );
    fa[0] = fb[0] = prea[0] = preb[0] = 1;

    for(int i=2;i<=n;i++)
    {
        while( ida+1<=i-2&&a[i]-a[ida+1]>=x )   ida++;//目前可转移的最大的索引,可以从[0,ida]转移而来
        while( idb+1<=i-2&&a[i]-a[idb+1]>=y )   idb++;
        fa[i-1] = get( bzero,ida,preb );
        fb[i-1] = get( azero,idb,prea );
        prea[i-1] = ( prea[i-2]+fa[i-1] )%mod;
        preb[i-1] = ( preb[i-2]+fb[i-1] )%mod;
        if( a[i]-a[i-1]<x )
        {
            for(int j=azero;j<=i-2;j++) fa[j] = 0;
            azero = i-1;
        }
        if( a[i]-a[i-1]<y )
        {
            for(int j=bzero;j<=i-2;j++) fb[j] = 0;
            bzero = i-1;
        }
    }
    int ans = 0;
    for(int i=0;i<=n;i++)
        ans = ( ans+fa[i]+fb[i] )%mod;
    cout << ( ans%mod+mod )%mod;
}

线段树慢一点, O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))

#include <bits/stdc++.h>
using namespace std;
#define mid (l+r>>1)
#define ls (rt<<1)
#define rs (rt<<1|1)
#define lson ls,l,mid
#define rson rs,mid+1,r
#define int long long
const int maxn = 1e6+10;
const int mod = 1e9+7;
int n,x,y,a[maxn];
int azero,bzero,ida,idb;

struct Line_Tree
{
    int sum[maxn];
    void insert(int rt,int l,int r,int id,int val)
    {
        if( r<id || l>id )  return;
        if( l==r && l==id ){ sum[rt] = val; return; }
        insert(lson,id,val); insert(rson,id,val);
        sum[rt] = ( sum[ls]+sum[rs] )%mod;
    }
    int ask(int rt,int l,int r,int L,int R)
    {
        if( l>R || r<L )    return 0ll;
        if( l>=L&&r<=R )    return sum[rt];
        return ( ask(lson,L,R)+ask(rson,L,R) )%mod;
    }
}t1,t2;
signed main()
{
    scanf("%lld%lld%lld",&n,&x,&y);
    for(int i=1;i<=n;i++)    scanf("%lld",&a[i] );
    sort( a+1,a+1+n );
    t1.insert(1,0,n,0,1); t2.insert(1,0,n,0,1);
    for(int i=2;i<=n;i++)
    {
        while( ida+1<=i-2&&a[i]-a[ida+1]>=x )   ida++;//目前可转移的最大的索引,可以从[0,ida]转移而来
        while( idb+1<=i-2&&a[i]-a[idb+1]>=y )   idb++;
        int tempa = 0, tempb = 0;
        if( ida>=bzero ) tempa = t2.ask( 1,0,n,bzero,ida );
        if( idb>=azero ) tempb = t1.ask( 1,0,n,azero,idb );  
        t1.insert(1,0,n,i-1,tempa ); t2.insert(1,0,n,i-1,tempb );      
        if( a[i]-a[i-1]<x )//当然这里可以线段树直接区间赋值0,不过直接暴力就好了 
        {
            for(int j=azero;j<=i-2;j++) t1.insert(1,0,n,j,0);
            azero = i-1;
        }
        if( a[i]-a[i-1]<y )
        {
            for(int j=bzero;j<=i-2;j++) t2.insert(1,0,n,j,0);
            bzero = i-1;
        }
    }
    cout << ( t1.sum[1]+t2.sum[1] )%mod;
}