【gdgzezoi】Problem A: 玩具谜题

Description

小南有一套可爱的玩具小人,它们各有不同的职业。

有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。如下图:

这时 singer 告诉小南一个谜题:“眼镜藏在我左数第 3 个玩具小人的右数第 1 个玩具小人的左数第 2 个玩具小人那里。”

小南发现,这个谜题中玩具小人的朝向非常关键,因为朝内和朝外的玩具小人的左右方向是相反的: 面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向;而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向。

小南一边艰难地辨认着玩具小人,一边数着:

“singer 朝内,左数第 3 个是 archer。

“archer 朝外,右数第 1 个是 thinker。

“thinker 朝外,左数第 2 个是 writer。

“所以眼镜藏在 writer 这里!”

虽然成功找回了眼镜,但小南并没有放心。如果下次有更多的玩具小人藏他的眼镜,或是谜题的长度更长,他可能就无法找到眼镜了。为了解决问题,他决定让他的玩具小人互相战斗,杀死对方,以减少玩具小人的数量,这样就不会有那么多麻烦了。

小南一共有N个玩具小人,每个玩具小人有一个战斗力,小南会把一些玩具小人染成红色(也可以不选,也就是全部染成蓝色;也可以全部染成红色),剩下的都染成蓝色,让红色方和蓝色方互相战斗(其中一方可以为空)。

当同一方的两个玩具小人的战斗力差距过大时,他们之间会产生矛盾。为了有效减少玩具小人的数量,小南希望他的玩具小人之间尽可能地产生矛盾。因此,任意两个不同的红色玩具小人的战斗力之差必须大于等于一个给定的数A,而任意两个不同的蓝色玩具小人的战斗力之差则必须大于等于另一个给定的数B。

小南想知道有多少可能的染色方案,使得同一方的玩具小人两两之间都会产生矛盾。请你写一个程序帮他统计所有可能的方案的总数,数值对109+7取模。

Input

输入第一行包含三个数N,A,B。
接下来N行中,第i行包含一个整数ai,表示第i个玩具小人的战斗力。

Output

输出染色方案数对109+7取模后的值。
Sample Input

Sample Input 1

5 3 7
1
3
6
9
12

Sample Input 2

7 5 3
0
2
4
7
8
11
15

Sample Input 3

8 2 9
3
4
5
13
15
22
26
32

Sample Input 4

3 3 4
5
6
7
Sample Output

Sample Output 1

5

Sample Output 2

4

Sample Output 3

13

Sample Output 4

0

HINT

在第一个样例中,记红色小人集合为X,蓝色为Y,则有一下5种染色方案:

  1. X={1,6,9,12},Y={3}
  2. X={1,6,9},Y={3,12}
  3. X={3,6,9,12},Y={1}
  4. X={3,6,9},Y={1,12}
  5. X={3,6,12},Y={1,9}

数据范围:
1≤N≤105
1≤A,B≤1018
0≤ai≤1018
ai<ai+1(1≤i<N)

存在30分的子任务,满足1≤N≤2000。

思路

首先考虑O(n2)的做法:

为了方便,我们令a0=−∞

设fM,i,j(M∈{X,Y},1≤i≤n,0≤j<i)表示已划分好S1⋯i且Sj是最后一个不属于M的元素的方案数

已算好fX,1⋯i,j和fY,1⋯i,j,如何转移?

①若ai+1−ai≥A,Si+1可被放入X中,则fX,i+1,0⋯i−1=fX,i,0⋯i−1

否则ai,ai+1不可一起被放入X中,fX,i+1,0⋯i−1=0

②显然fY,i+1,i=∑j=0i−1[ai+1−Sj≥B]fX,i,j

对fY的处理相似

最后的答案就是∑i=0n−1fX,n,i+∑i=0n−1fY,n,i

考虑优化

首先将a升序排序

因为a是递增的,所以满足ai+1−aj≥B的Sj一定是一段前缀,所以我们可以用二分找到右端点并用线段树求区间和

其他转移就相当于线段树的单点更新

代码

#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=1e5+77;
int sumx[N*4],sumy[N*4],lazx[N*4],lazy[N*4],*laz,*sum,n;
ll a[N];
void pushdown(int x)
{
    if(laz[x])
    {
        laz[x<<1]=laz[x<<1|1]=1;
        sum[x<<1]=sum[x<<1|1]=0;
        laz[x]=0;
    }
}
int query(int st,int ed,int l,int r,int x)
{
    if(st<=l&&r<=ed)return sum[x];
    pushdown(x);
    int mid=(l+r)>>1,ans=0;
    if(st<=mid)ans=(ans+query(st,ed,l,mid,x<<1))%mod;
    if(mid<ed)ans=(ans+query(st,ed,mid+1,r,x<<1|1))%mod;
    return ans;
}
void add(int pos,int v,int l,int r,int x)
{
    if(l==r)
    {
        sum[x]=(sum[x]+v)%mod; return;
    }
    pushdown(x);
    int mid=(l+r)>>1;
    if(pos<=mid) add(pos,v,l,mid,x<<1);
    else add(pos,v,mid+1,r,x<<1|1);
    sum[x]=(sum[x<<1]+sum[x<<1|1])%mod;
}
int queryx(int st,int ed)
{
    laz=lazx; sum=sumx;
    return query(st,ed,0,n-1,1);
}
void addx(int pos,int v)
{
    laz=lazx; sum=sumx;
    add(pos,v,0,n-1,1);
}
int queryy(int st,int ed)
{
    laz=lazy; sum=sumy;
    return query(st,ed,0,n-1,1);
}
void addy(int pos,int v)
{
    laz=lazy; sum=sumy;
    add(pos,v,0,n-1,1);
}
int main()
{
    ll A,B;
    scanf("%d%lld%lld",&n,&A,&B);
    for(int i=1; i<=n; i++)scanf("%lld",&a[i]);
    sort(a+1,a+n+1);
    a[0]=-4223372036854775807ll;
    addx(0,1); addy(0,1);
    for(int i=1; i<n; i++)
    {
        int l=0,r=i-1,x=0;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(a[i+1]-a[mid]>=B) x=mid,l=mid+1;else r=mid-1;
        }
        int t1=queryx(0,x);
        l=0; r=i-1;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(a[i+1]-a[mid]>=A) x=mid,l=mid+1;else r=mid-1;
        }
        int t2=queryy(0,x);
        if(a[i+1]-a[i]<A)
        {
            sumx[1]=0; lazx[1]=1;
        }
        if(a[i+1]-a[i]<B)
        {
            sumy[1]=0; lazy[1]=1;
        }
        addy(i,t1); addx(i,t2);
    }
    printf("%d",(queryx(0,n-1)+queryy(0,n-1))%mod);
}
发布了703 篇原创文章 · 获赞 392 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/Eric1561759334/article/details/100835844