[AT2292] [AGC009 C] Division into Two

26 篇文章 0 订阅
19 篇文章 0 订阅
洛谷传送门
Atcoder传送门

题目大意

给定 n n n个不同的整数 a 1 , a 2 , a 3 , . . . , a n a_1,a_2,a_3,...,a_n a1,a2,a3,...,an,求将它们分成两个集合 X , Y X,Y X,Y,并且 X X X集合中任意两个数的差 ≥ A \ge A A Y Y Y集合中任意两个数的差 ≥ B \ge B B的方案数。

输入输出格式

输入格式

第一行三个正整数 n , A , B n,A,B n,A,B

以下 n n n行, 每行一个正整数 a i a_i ai

输出格式

一行一个非负整数, 表示方案数 m o d   1000000007 mod\ 1000000007 mod 1000000007

输入输出样例

输入样例#1:
5 3 7
1
3
6
9
12
输出样例#1:
5
输入样例#2:
7 5 3
0
2
4
7
8
11
15
输出样例#2:
4
输入样例#3:
8 2 9
3
4
5
13
15
22
26
32
输出样例#3:
13

解题分析

还是挺好想的。

在这里为了方便设 A ≤ B A\le B AB, 同时将 a i a_i ai从小到大排序。

首先如果存在 i ∈ [ 3 , n ] i\in [3,n] i[3,n]满足 a i − a i − 2 &lt; A ​ a_i-a_{i-2}&lt;A​ aiai2<A, 就一定无解。

d p [ i ] dp[i] dp[i]表示一定取 a i a_i ai B B B集合的方案数, 那么对于 d p [ i ] dp[i] dp[i]转移区间一定是一段连续的区间 [ l , r ] [l,r] [l,r], 满足 ∀ i ∈ [ l + 2 , i − 1 ] , a i − a i − 1 ≥ A \forall i\in[l+2,i-1], a_i-a_{i-1}\ge A i[l+2,i1],aiai1A a i − a r ≥ B a_i-a_{r}\ge B aiarB。因为我们保证了 A ≤ B A\le B AB, 所以不需要关心 a i − a r ≥ A a_i-a_r\ge A aiarA的问题。

这样预处理出来每个位置能向左连续多少个满足两两位置差 ≥ A \ge A A, 就可以 O ( N ) O(N) O(N)DP了。

代码如下:

#include <cstdio>
#include <cmath>
#include <climits>
#include <cstring>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MOD 1000000007
#define MX 100500
#define ll long long
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n;
ll A, B, dat[MX];
int dp[MX], sum[MX], lef[MX];
IN int get(R int l, R int r)
{
	if (l > r) return 0;
	if (l <= 0) return sum[r];
	return (sum[r] - sum[l - 1] + MOD) % MOD;
}
int main(void)
{
	in(n), in(A), in(B);
	if (A > B) std::swap(A, B);
	for (R int i = 1; i <= n; ++i) in(dat[i]);
	std::sort(dat + 1, dat + 1 + n);
	dat[0] = -1e18;
	for (R int i = 3; i <= n; ++i) if (dat[i] - dat[i - 2] < A) return puts("0"), 0;
	for (R int i = 1; i <= n; ++i)
	{
		if (dat[i] - dat[i - 1] >= A) lef[i] = lef[i - 1];
		else lef[i] = i;
	}
	dp[0] = sum[0] = 1;
	int cur = 0, ans = 0;
	for (R int i = 1; i <= n; ++i)
	{
		W (dat[i] - dat[cur + 1] >= B) ++cur;
		dp[i] = get(lef[i - 1] - 1, cur);
		sum[i] = (sum[i - 1] + dp[i]) % MOD;
	}
	for (R int i = n; ~i; --i)
	{
		(ans += dp[i]) %= MOD;
		if (i != n && dat[i + 1] - dat[i] < A) break;
	}
	printf("%d", ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值