AGC009C Division into Two

题意

有\(n\)个严格升序的数,请你分成两个集合\(A\)和\(B\),其中一个集合任意两数之差不小于\(x\),另一集合任意两数之差不小于\(y\)。
问方案数,集合可以为空。
$n \le 10^5 $
传送门

思路

又是一道神仙\(dp\)
设\(dp_i\)表示当前\(B\)集合的最后一个数是\(a_i\)的方案数。
如果暴力转移就是:\[dp_i=\sum_{j<i \& a_i-a_j\ge y}dp_j\]
并且满足区间\([j+1,i-1]\)能够放在\(A\)集合中
可以发现,满足条件的\(j\)是一个区间,因此前缀和优化,最后把答案累加起来就好了。

代码十分简短

#include <bits/stdc++.h>
const int N=100005,mu=1000000007;
int n,s[N],dp[N],l=0,r=0;
long long x,y,a[N];
int main(){
    scanf("%d%lld%lld",&n,&x,&y);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    if (x>y) std::swap(x,y);
    for (int i=1;i+2<=n;i++) 
        if (a[i+2]-a[i]<x){
            puts("0");return 0;
        } 
    dp[0]=s[0]=1;
    for (int i=1;i<=n;i++){
        while (a[i]-a[r+1]>=y && r<i-1) r++;
        if (l<=r){
            if (l) dp[i]=(s[r]-s[l-1]+mu)%mu;
            else dp[i]=s[r];    
        } 
        if (a[i]-a[i-1]<x) l=i-1;
        s[i]=(s[i-1]+dp[i])%mu;
    }
    int ans=0;
    for (int i=n;i>=0;i--){
        ans=(ans+dp[i])%mu;
        if (a[i+1]-a[i]<x && i<n) break;
    }
    printf("%d",ans);
}

后记

我好菜啊。以后写\(Atcoder \space dp\)的时候都可以加上思路的第一和最后一句了

上一篇:399. Evaluate Division


下一篇:HDU3017 Treasure Division 题解 折半搜索