P2687 题解
Register_int · · 题解
首先求出以
之后计算最长下降子序列的个数
不过这样只能统计 天数序列 不同的方案,而题目要求 元素不同。所以,对于相同的
为方便理解给个代码实现:
for (int i = 1; i <= n; i++) {
f[i] = dp[i] = 1;
for (int j = 1; j < i; j++) {
if (a[i] < a[j]) {
if (dp[i] == dp[j] + 1) f[i] += f[j];
else if (dp[i] < dp[j] + 1) dp[i] = dp[j] + 1, f[i] = f[j];
} else if (a[i] == a[j]) f[j] = 0;
}
ans = max(ans, dp[i]);
}
结束……了吗?
之前很多题解到这一步就结束了,因为他们认为这个做法的复杂度是
为什么呢?先来考虑答案最大是多少:构造每两个数为一组,组内递增,每组的最小值递减。这样可以得出极限答案
下面给出两种解决方法:
1. 压位高精
考虑物理上减少答案位数。极限数据下答案大约为
我由于实现垃圾使用的是压
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 5e3 + 10;
const int MAXM = 42;
const ll BASE = 1e18;
struct Int {
ll a[MAXM];
Int(int k = 0) {
for (int i = MAXM - 1; ~i; i--) a[i] = k % BASE, k /= BASE;
}
Int operator + (const Int &rhs) const {
ll t = 0; Int res = *this;
for (int i = MAXM - 1; ~i; i--) {
t += res.a[i] + rhs.a[i];
res.a[i] = t % BASE, t /= BASE;
}
return res;
}
friend ostream & operator << (ostream &out, const Int &n) {
int p = 0;
for (; p < MAXM && !n.a[p]; p++);
if (p == MAXM) return out << 0;
for (out << n.a[p++]; p < MAXM; p++) out << setw(18) << setfill('0') << n.a[p];
return out;
}
Int & operator += (const Int &rhs) { return *this = *this + rhs; }
};
int n, a[MAXN], ans;
int dp[MAXN]; Int f[MAXN], sum;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
for (int i = 1; i <= n; i++) {
f[i] = dp[i] = 1;
for (int j = 1; j < i; j++) {
if (a[i] < a[j]) {
if (dp[i] == dp[j] + 1) f[i] += f[j];
else if (dp[i] < dp[j] + 1) dp[i] = dp[j] + 1, f[i] = f[j];
} else if (a[i] == a[j]) f[j] = 0;
}
ans = max(ans, dp[i]);
}
for (int i = 1; i <= n; i++) if (dp[i] == ans) sum += f[i];
cout << ans << " " << sum;
}
2. 树状数组
计算最优方案的部分并不需要优化,需要优化的是方案数。
没有“元素不同”要求的情况下,实际上只需要以长度作为下标来记录方案数总和就能解决。有元素不同时,可以使用哈希表来记录
但是会多算
由于树状数组需要离散化,哈希表可以省掉。
代码不放了。