[USACO19JAN,Platinum]Train Tracking 2 神仙結論 + dp + 複習快速冪

題目描述

每天都有火車從農場前經過,它有N個車廂(1≤N≤100000),每個車廂都有一個在1到1e9之間的數字,不同的車廂也有可能有相同的數字。
通常,Bessie看火車經過時,會觀察車廂上的數字,但今天有霧,Bessie看不到任何的數字。幸運的是,她已經通過可靠來源知道車廂的所有滑動窗口的最小值,即,她知道一個正整數K和N-K+1的數字,第i個數字Ci表示第i個車廂到第i+K-1這K個車廂中所有數字的最小值。
請你幫助Bessie求出滿足這個最小值的車廂的編號方法數量,由於這個數字可能很大,答案對1e9+7取模。
題目保證至少有一種符合編號的情況。

輸入格式
第一行輸入兩個數N和K,接下來N-K+1行,每行輸入一個數Ci。

輸出格式
輸出一個整數,表示答案對1e9+7取模的結果。

樣例輸入輸出

Sample Input 1
4 2
999999998
999999999
999999998
Sample Output 1
3

博文鏈接

自認爲寫的還比較清楚的一篇博文

題解

看了別人的博客,自己也手癢來擼一篇
初拿到這道題,我是懵逼的。感覺沒有一個點是定的,然後,再仔細一想,如果左邊一個點的區間和右邊一個點區間的最小值不一樣,那麼我們可以定下來一個位置的值(相對比較固定)
在這裏插入圖片描述考慮完了不同的情況,那麼相同的情況呢?
在這裏插入圖片描述
此時,你可以碼出核心代碼了 :

//dp[i] = (1e9 - c[i] + 1) * dp[i - 1] - (1e9 - c[i]) ^ k *dp[i - k - 1]
LL sovle (const int val, const int len) {
	LL x = Max - val;
	LL power = qkpow (x, k);
	dp[0] = dp[1] = 1;
	for (int i = 2; i <= len + 1; ++ i) {
		dp[i] = 1ll * (x + 1) * dp[i - 1] % mod;
		if (i - k > 0)
			dp[i] = (dp[i] - power * dp[i - k - 1] % mod + mod) % mod;
	}
	return dp[len + 1];
}

在得到狀態轉移方程後,就需要注意不同數值的交界處;
從第一張圖來看,我們可以定下來的值都是較小的那一個,那麼考慮兩種情況:
對於區間 [i, j],
①c[i - 1] > c[i] : 我們需要將區間 [i, i + k - 2] 交給i - 1號位負責,所以本次需要考慮的區間長度少了 k - 1;同時,第 i 號位的值是可以確定的,因此需要考慮的區間長度又少了一位,一共少了 k 位;
②c[j] < c[j + 1] : 同理,區間長度少了 k 位
特別值得注意的是,在考慮首尾元素的時候,區間的前後是否需要減去重合元素。

好了,思路都理清楚了,還請各位不要像蒟蒻一樣打錯快速冪 (祠堂罰跪ing)

參考代碼

#include <cstdio>
#include <iostream>
using namespace std;
#define LL long long

const int N = 1e5;
const int Max = 1e9;
const int mod = 1e9 + 7;
int n, k, minn[N + 5];
LL dp[N + 5], ans = 1;

LL qkpow (const LL x, const int p) {
	if (p == 1)
		return x;
	if (p == 0)
		return 1ll;
	if (p & 1)
		return qkpow (x * x % mod, p >> 1) % mod * x % mod;
	return qkpow (x * x % mod, p >> 1) % mod;
}

LL sovle (const int val, const int len) {
	LL x = Max - val;
	LL power = qkpow (x, k);
	dp[0] = dp[1] = 1;
	for (int i = 2; i <= len + 1; ++ i) {
		dp[i] = 1ll * (x + 1) * dp[i - 1] % mod;
		if (i - k > 0)
			dp[i] = (dp[i] - power * dp[i - k - 1] % mod + mod) % mod;
	}
	return dp[len + 1];
}

int main () {
	//freopen ("train.in", "r", stdin);
	//freopen ("train.out", "w", stdout);
	
	scanf ("%d %d", &n, &k);
	for (int i = 1; i <= n - k + 1; ++ i)
		scanf ("%d",&minn[i]);
	
	for (int i = 1; i <= n - k + 1; ++ i) {
		int j = i;
		while (minn[j + 1] == minn[i])
			++ j;
		int len = j - i + k;
		if (i != 1 && minn[i - 1] > minn[i])
			len -= k;
		if (j != n - k + 1 && minn[j + 1] > minn[i])
			len -= k;
		if (len > 0)
			ans = ans * sovle ( minn[i], len ) % mod;
		i = j;
	}
	printf ("%lld\n", ans);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章