Luogu P10066 Solution
闲话
时隔许久,我终于又独立做对了一道蓝题,所以写一篇题解。
(但这道题真的是蓝题吗)
题解
首先对题目性质进行一个简单的分析。
设原数组为 \(a\),SMS 数组为 \(b\),下标从 \(0\) 开始,可以列出以下的算式:
\[b_x=\sum_{i=x}^{x+k-1} a_i
\]
此时,我们将 \(0\) 和 \(1\) 代入式子,发现它们的大小关系只会被 \(a_0\) 和 \(a_k\) 的大小关系影响。为什么?因为,它们的求和范围的区别有且仅有这两个元素。
于是,我们可以发现一个重要的性质:
在给定数组 \(b\) 且合法的情况下,确定数组 \(a\) 的前 \(k\) 个元素等同于确定整个数组 \(a\)。
那么,怎么利用这个性质呢?
首先,注意到一个事实:由于根据 \(b\) 数组决定的 \(a\) 下标同余元素的大小关系有等于、小于、大于三种,并且保证 \(b\) 至少对应一个合法的 \(a\),且 \(a\) 仅包含 \(0\) 和 \(1\),所以对于 \(0\) 到 \((k-1)\) 中任意一个对 \(k\) 取模的结果,只要有其中一个确定了非等于的关系,那么所有同余下标的元素实际上都被锁死了。
于是,在对整个 \(b\) 数组做完上述的判断后,我们会发现此时 \(a\) 的前 \(k\) 个元素有三种取值:\(0\),\(1\),\(0/1\)。
此时,依据 \(b_0\) 的值,我们可以容易地使用组合数确定可选方案的个数。
时间复杂度 \(O(n)\)。
代码
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e6 + 10, mod = 1e6 + 3;
int n, k, a[N], req[N], rn;
using ll = long long;
ll fac[N];
inline ll fsp(ll x, ll y)
{
ll res = 1;
while (y)
{
if (y & 1)
res = res * x % mod;
x = x * x % mod;
y >>= 1;
}
return res;
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i <= n - k; i++)
{
scanf("%d", a + i);
}
memset(req, -1, k << 2);
for (int i = 0; i < n - k; i++)
{
if (a[i] != a[i + 1] and !~req[i % k])
{
req[i % k] = (a[i] > a[i + 1]);
}
}
for (int i = 0; i < k; i++)
{
if (~req[i])
a[0] -= req[i];
else
rn++;
}
fac[0] = 1;
for (int i = 1; i <= rn; i++)
{
fac[i] = fac[i - 1] * i % mod;
}
printf("%lld\n", fac[rn] * fsp(fac[a[0]], mod - 2) % mod * fsp(fac[rn - a[0]], mod - 2) % mod);
}