洛谷 CF466D Increase Sequence 蓝 题解

前言

看了题解才会的……决定自己详细写一篇保证自己学明白了!

思路

题目给我印象最深的一句话是:任意两个区间的左右端点不重合。感觉这个要求挺苛刻的,你会发现 \(a\) 数组的第 \(1\) 项和最后一项最多和 \(h\) 相差 \(1\)

这个不难理解,因为想要让第一项 \(+1\),你的左端点必须是 \(1\),那么以后的所有操作的左端点都不能是 \(1\) 了,自然只能操作一次,最后一项同理。

所以决定因素在于 \(a_i\)\(h\) 相差多少,那么我们把 \(a_i\) 的意义变一下,如果原序列为 \(a_i\),那么新的 \(a_i\) 表示 \(h - a_i\)(接下来的所有 \(a_i\) 表示的均为最新意义)

分析每次操作,改变的是一个区间且加的值是固定的,如果暴力修改复杂度不可承受,考虑有没有一种 \(O(1)\) 的修改方式?差分!

\(d_i=a_i-a_{i-1}\),那么每次区间为 \([l,r]\) 的操作,等价于 \(d_l-1,d_{r+1}+1\),由于端点不能重复,所以每个 \(d_i\) 至多会被一次,那么无解情况就出来了:若存在 \(|d_i|>1\),必然无解!(请注意,现在的操作为区间减,因为 \(a\) 数组的意义已经变了)

最终合法的状态为:\(d\) 数组全为 \(0\),而可能的 \(d_i\) 的值也就 \(3\) 种:\(1,-1,0\),逐个分析即可。

  • \(d_i\)\(1\),说明必须要有一个操作的左端点为 \(i\),也就是说左端点数量要加一
  • \(d_i\)\(-1\),说明必须要有一个操作的右端点为 \(i\),那么这个右端点可以和之前的任何一个左端点匹配,完毕后左端点数量要减一,因为一个端点不能同时做两次左端点
  • \(d_i\)\(0\),该怎么办?在此之前,我想先明确一下题意,题意指:一个端点不能作多次左端点,也不能作多次右端点,但是可以作一次左端点再作一次右端点。所以对于 \(0\) 的情况,要么先让它作右端点,和之前的任意一个左端点匹配,然后再作左端点,来保证 \(0-1+1=0\);要么干脆就不动它,即不对其进行操作。请注意:这里不需要改变左端点数量,因为在第一种情况中既作右端点又作左端点就抵消了,第二种情况下无事发生……

Code

#include <iostream>

using namespace std;

typedef long long LL;
const int N = 2010;
const LL mod = 1e9 + 7;

int n, h, num;
int a[N], diff[N];
LL ans = 1;

int main()
{
	cin >> n >> h;
	for (int i = 1; i <= n; i ++ )
	{
		cin >> a[i];
		a[i] = h - a[i];
	}
	for (int i = 1; i <= n + 1; i ++ )
	{
		diff[i] = a[i] - a[i - 1];
		if (abs(diff[i]) > 1) return cout << 0 << endl, 0;
	}
	for (int i = 1; i <= n; i ++ )
	{
		if (diff[i] == 1) num ++ ;
		if (diff[i] == 0) ans = ans * (num + 1) % mod;
		if (diff[i] == -1) ans = ans * num % mod, num -- ;
	}
	cout << ans << endl;
	return 0;
}

后记

花了好长时间才理解,完结撒花!

posted @ 2022-10-19 08:33  LittleMoMol  阅读(107)  评论(0编辑  收藏  举报
*/