标签
lis + dp
思路
从题目描述来看,这显然是一道 lis 问题, 如果不考虑背包的承重的话,可以直接按重量排序,然后按价值做最长上升子序列。
如果考虑上背包承重,那么最长上升自序列的长度不能超过可以使用的背包数, 显然大背包要用来放大物品才不会有浪费, 所以把物品先按重量从大到小排序, 再按价值做最长下降子序列, 然后考虑对于求出的以 i 结尾的最长下降子序列, 必须能够放到背包里. 所以以 i 结尾的可以放到背包里的最长下降子序列
f[i]=min(maxj=1i−1(f[j]+1),容量大于wi的背包个数) , 用树状数组可以优化到 O(nlgn) , 容量大于 wi 的背包个数可以用双指针来求.
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| #include <iostream> #include <cstdio> #include <algorithm> #include <cstring>
using namespace std;
const int N = 1e6 + 10; int T; int val[N]; pair<int, int> w[N]; int n, m; int t[N]; int tmp[N], tot;
int lowbit(int x) { return x & (-x); }
int query(int x) { x = n - x + 1; int res = 0; for (int i = x; i; i -= lowbit(i)) { res = max(res, val[i]); } return res; }
void change(int x, int v) { x = n - x + 1; for (int i = x; i <= n; i += lowbit(i)) { val[i] = max(val[i], v); } }
bool cmp(int a, int b) { return a > b; }
bool cmp2(pair<int, int> a, pair<int, int> b) { if (a.first == b.first) return a.second > b.second; return a.first > b.first; }
int f[N];
int main() { scanf("%d", &T); while (T--) { memset(val, 0, sizeof(val)); scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d%d", &w[i].first, &w[i].second); tmp[i] = w[i].second; } sort(tmp + 1, tmp + n + 1); for (int i = 1; i <= n; ++i) { w[i].second = lower_bound(tmp + 1, tmp + n + 1, w[i].second) - tmp; } scanf("%d", &m); for (int i = 1; i <= m; ++i) { scanf("%d", &t[i]); } sort(t + 1, t + m + 1, cmp); sort(w + 1, w + n + 1, cmp2); int j = 0; for (int i = 1; i <= n; ++i) { while (j + 1 <= m && t[j + 1] >= w[i].first) { ++j; } f[i] = min(j, query(w[i].second) + 1); change(w[i].second, f[i]); } int ans = 0; for (int i = 1; i <= n; ++i) { ans = max(ans, f[i]); } printf("%d\n", ans); } return 0; }
|
总结
这题本质上是在求有限制的最长上升子序列, 只是运用到了大物品放到大背包中的贪心思想.
标签
dp + 计数 + 二项式系数
思路
先想一个错误的 dp , 设 f[i][j] 表示大小为 i 的树, 深度小于等于 j 的方案数. 然后可以写出一个错误的 dp , f[i][d]=∑j=1i−1f[i−j][d]+f[j][d−1](ji−1) , 但这样是会有重复的 , 所以我们要设转移中的 j 表示以次大的编号为根的子树的大小. 这样就可以防止重复.
f[i][d]=∑j=1i−1f[i−j][d]+f[j][d−1](j−1i−2)
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <iostream> #include <cstdio> #define int long long
using namespace std;
const int N = 550; const int mod = 998244353; int f[N][N]; int l, r; int C[N][N]; int n, m; int vis[N];
int add(int a, int b) { return a + b >= mod ? a + b - mod : a + b; }
signed main() { scanf("%lld%lld", &n, &m); for (int i = 0; i <= n; ++i) C[i][0] = 1; for (int i = 1; i <= n; ++i) { for (int j = 1; j <= n; ++j) { C[i][j] = add(C[i - 1][j - 1], C[i - 1][j]); } } for (int i = 1; i <= m; ++i) { int x; scanf("%lld", &x); vis[x] = 1; } scanf("%lld%lld", &l, &r); f[0][0] = 1; for (int i = 1; i <= n; ++i) { f[1][i] = 1; f[0][i] = 1; } for (int i = 2; i <= n; ++i) { for (int j = 1; j <= n; ++j) { for (int k = 1; k < i; ++k) { if (!vis[k]) f[i][j] = add(f[i][j], f[i - k][j] * f[k][j - 1] % mod * C[i - 2][k - 1] % mod); } } } for (int i = l; i <= r; ++i) { if (vis[n]) { printf("0 "); continue; } printf("%lld ", (f[n][i] - f[n][i - 1] + mod) % mod); } return 0; }
|
总结
去重好方法, 限制插入过程中被插入的元素的性质, 使得到的情况不重不漏.
标签
哈希 + 前缀和
思路
先想怎么判断一个序列是否是括号序列, 从第一位开始如果栈顶元素和当前元素相等就弹栈, 否则把当前元素压入栈中. 最后判断栈是否为空即可.
考虑 O(n2) 做法 , 枚举左端点, 模拟判断的过程, 如果在某一位栈为空, 那么 ans++
, 答案就是栈为空的子段个数.
那么可以考虑前缀和, 如果两个前缀的栈是一样的, 那么它们直接就是一个空栈, 所以只用从 1 开始对整个序列做一次操作, 然后把栈的哈希值排序, 这样就可以得到有多少对栈是相等的.
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define int long long #define ull unsigned long long
using namespace std;
const int N = 1e6 + 10; const int p = 432421; char s[N]; char stk[N]; int invp; int tot; int n; int ans = 0; int now; ull f[N]; int id[N];
int qpow(int a, int b, int mod) { int res = 1; while (b) { if (b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; }
int add(int a, int b) { return a + b >= 998244353 ? a + b - 998244353 : a + b; }
signed main() { scanf("%s", s + 1); n = strlen(s + 1); for (int i = 1; i <= n; ++i) { if (!tot || stk[tot] != s[i]) { stk[++tot] = s[i]; f[i] = f[i - 1] * p + s[i]; id[tot] = i; } else { --tot; f[i] = f[id[tot]]; } } sort(f, f + n + 1); int cnt = 0; for (int i = 0; i <= n; ++i) { if (f[i] == f[i + 1]) { ++cnt; } else { ++cnt; ans = add(ans, cnt * (cnt - 1) % 998244353 * qpow(2, 998244353 - 2, 998244353) % 998244353); cnt = 0; } } cout << ans << endl; return 0; }
|