HDU-6806(DP & 神秘乱搞做法)
点开题目看困了先睡了一觉。
睡醒了阅读一遍题目以为是组合数学吓哭了跑去看别的题了。
回来了之后一秒看出来是 DP,然后花了十五分钟推了个错误的式子,推累了歇了会儿。
真不喜欢写 DP 于是开始乱搞,还真让我搞出来了。
这我高低写篇题解纪念一下,以下是我的科研成果。
思路 1:DP
注意到每个单词有两个状态:和前面的单词交换或不变,当这个单词和前面那个单词不同时交换才有意义。
设 \(f_{i, 0 / 1}\) 表示考虑前 \(i\) 个单词组成的句子,第 \(i\) 个单词不进行交换或进行交换的答案。
初始状态:\(f_{1, 0} = 1, f_{1, 1} = 0\)。很好理解,毕竟总不能让一个单词和自己交换。
如果当前单词不进行操作,那么无论上一个单词怎么样都和它没关系,直接将两种情况的方案数继承下来,即 \(f_{i, 0} = f_{i - 1, 0} + f_{i - 1, 1}\)。如果进行交换操作,上一个单词肯定不能交换,此时有 \(f_{i, 1} = f_{i - 1, 0}\)。但是如果当前单词和上一个单词一样,那交换后并不能产生新的答案,则有 \(f_{i, 1} = 0\)。
答案就是 \(f_{n, 1} + f_{n, 0}\),注意取模。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MN = 2e5 + 3, mod = 1e9 + 7;
int n, f[MN][2];
string s[MN];
void solve() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> s[i];
f[1][0] = 1, f[1][1] = 0;
for (int i = 2; i <= n; i++) {
f[i][0] = (f[i - 1][0] + f[i - 1][1]) % mod;
if (s[i] != s[i - 1]) f[i][1] = f[i - 1][0];
else f[i][1] = 0;
}
cout << (f[n][0] + f[n][1]) % mod << "\n";
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int T;
cin >> T;
while (T--) solve();
return 0;
}
思路 2:乱搞
首先相邻的单词如果是一样的是不会对答案产生贡献的,所以我们只考虑这个句子中每个相邻单词都不同的小的句子。方便起见,我们定义一个没有两个相同的单词相邻的句子为好句子。
显然这些好句子之间是互不影响的,我们设一个长度为 \(x\) 的好句子产生的答案为 \(f(x)\),一个完整句子的答案就是找出的所有的 \(x\) 的 \(f(x)\) 的乘积,那我们就需要求出 \(f(x)\) 的值。
然后。。。好像不会了。
别急我们先手模两组数据:
input:
4
n o i p
output:
5
input:
6
yi yi si wu yi si
output:
8
根据上面的定义,第一个数据的答案就是 \(f(4) = 5\),第二个是 \(f(5) = 8\)。
有些熟悉?这非常斐波那契。
虽然多手模几组就能发现确实如此,但是教练有言曰:棍母。其实我忘了是什么,反正大概意思是结论的证明非常重要,因此我们证明一下。
对于上面的第一个样例,假设我们知道了当 \(x\) 小于 \(4\) 时所有的 \(f(x)\) 值,我们考虑如何用已知值推出当前值。
我们尝试分割,得到 n | o i p。
发现此时的答案就是 \(f(1) \times f(x - 1) = f(x - 1)\),很好记录下来。
但是上面的分法遗漏了第一个单词交换的情况,那我们将它变成这样:o n | i p。
左半部分是固定的,我们只计算右半部分,容易发现此时答案是 \(f(x - 2)\)。
整理一下,得到 \(f(x) = f(x - 1) + f(x - 2)\),就是斐波那契数列。
然后我们就完美解决了这道题!
好像是比上面的方法快了一点
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int MN = 1e5 + 3, mod = 1e9 + 7;
int n, ans = 1;
string s[MN];
vector<int> ve;
int f[MN];
void f_fir() {
f[1] = 1, f[2] = 2;
for (int i = 3; i <= 100000; i++)
f[i] = (f[i - 1] + f[i - 2]) % mod;
}
void solve() {
cin >> n;
ve.clear();
ans = 1;
for (int i = 1; i <= n; i++) cin >> s[i];
int tmp = 1;
bool fl = 0;
for (int i = 2; i <= n; i++) {
if (s[i] == s[i - 1]) {
if (fl) ve.push_back(tmp), tmp = 1, fl = 0;
}
else tmp++, fl = 1;
}
if (fl) ve.push_back(tmp);
for (int x : ve) ans = (ans * f[x]) % mod;
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
f_fir();
int T;
cin >> T;
while (T--) solve();
return 0;
}

浙公网安备 33010602011771号