10.28
CSP-S 前倒数第二场模拟赛,直接一道都没切出来。。。
再不放信心赛真要没信心了。
t1
dp题。
显然对于每一次行动都是一个背包dp。
变种在于背包更换,更换后容量重新计算。
所以记 \(las_i\) 表示到第 \(i\) 个物品时最大价值,每次背包转移前先与 \(las\) 取 \(\max\) 即可(表示换背包)。
然后发现时间复杂度是 \(O(mnV)\) 的,约为4e9 。
不可过呜呜呜
赛时就到这里了(oj神机发力了,大数据有一多半跑过了,986ms左右,最终85pts)。
赛后茅塞顿开:
还有一个条件没用上!
背包的容量单调不减。
背包很多有1e5个,而值域很小只到200,也就是说有大量的背包是被浪费的,根本不用转移!
所以我们只需对后200个背包dp即可(因为后200个背包可代表所有背包)。
时间复杂度骤降为\(O(n^2V)\)。
所以赛后改了下范围就过了(甚至最优解第三,小常数发力了)。
赛后乱搞发现我的赛时代码(即\(O(mnV)\)版)经过卡常,在评测机心情好的情况下可以90pts(理论最高是95pts,两次取或),神机啊!(accoder机子就只有60pts好吧)
神机发力了
code
只改范围的赛时代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int V = 210;
int n, m;
int wei[V], val[V];
int spa[N];
int dp[V][V]; // i,j,k: m,ed,spa
int las[V];inline int max(int a, int b) { return a > b ? a : b; }signed main()
{freopen("axis.in", "r", stdin);freopen("axis.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m;for (int i = 1; i <= n; ++i)cin >> wei[i] >> val[i];for (int i = 1; i <= m; ++i)cin >> spa[i];for (int i = max(m - 200, 1); i <= m; ++i){for (int j = 1; j <= n; ++j) // ed{for (int k = spa[i]; ~k; --k){dp[j][k] = max(dp[j - 1][k], las[j]);if (k - wei[j] >= 0)dp[j][k] = max(dp[j][k], dp[j - 1][k - wei[j]] + val[j]);}for (int k = spa[i]; ~k; --k)las[j] = max(las[j], dp[j][k]);}}cout << dp[n][spa[m]] << "\n";return 0;
}
t2
神秘结论题。
没看出结论直接爆。
结论:
对于每个给对给定的左,右端点(\(l,r\)),答案为\(2*(r-l+1)-len-1\) ,其中 \(len\) 为选取的点的个数。
所以只需枚举 \(l,r\) 然后对于每个 \(l,r\) 找到最大的合法的 \(len\) 即可。
拿两个优先队列就可以维护(一定要注意顺序)。
code
嘟嘟哒哒
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 10;
const int inf = 1e9 + 7;
int n, k;
int w[N];
priority_queue<int, vector<int>, greater<int>> in;
priority_queue<int> out;signed main()
{freopen("circle.in", "r", stdin);freopen("circle.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> n >> k;int sum = 0, maxn = -inf;for (int i = 1; i <= n; ++i){cin >> w[i];maxn = max(maxn, w[i]);sum += (w[i] >= 0 ? w[i] : 0);}if (maxn >= k){cout << 0;return 0;}if (sum < k || k <= 0){cout << -1;return 0;}int ans = inf;for (int i = 1; i <= n; ++i){while (in.size())in.pop();while (out.size())out.pop();sum = 0;int len = 0;for (int j = i; j <= n; ++j){// cerr << "j=" << j << "\n";sum += w[j], ++len;if (w[j] < 0)in.push(w[j]);while (in.size() && sum < k){// cerr << "j=" << j << "\n";sum -= in.top(), out.push(in.top()), in.pop(), --len;}if (sum < k)continue;// cerr << "!!j=" << j << " siz=" << out.size() << "\n";while (out.size() && sum + out.top() >= k){// cerr << "%%%j=" << j << "\n";sum += out.top(), in.push(out.top()), out.pop(), ++len;}ans = min(ans, (j - i + 1) * 2 - len - 1);}}cout << ans;return 0;
}
t3
赛时被环卡了 1h.
赛后才发现原来题也读假了。。。
原题面中的字典序不是针对字符的,而是指各编号的大小比较,就是按数值比。
我场上还将特意所有编号转成字符串然后排序。
正解:
发现\(n,m\)都很小,甚至\(O(nq)\)也是可接受的。
于是考虑直接暴力做。
但是在线判断贪心取最小的同时判断环还是太困难了(我不会),考虑先处理部分信息。
先对每个点dfs将连通处理出来,之后记\(f_{j,i}\)表示从\(j\)到\(i\)的路径上最小的点。
在之后查询时因为已知终点,所以可以直接将路径复现。
开个数组将路径存一下就行了。
还是注意特判。
时间复杂度\(O(n(n+m+q))\),虽然看着很可怕但它大概8e8,所以可过。
附唐诗记录
code
哩哩啦啦
#include <bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10;
int n, m, q, type;
int vis[N][N];
int f[N][N];
vector<int> e[N];
int flag[N], stk[N], top, las;inline void dfs(int x, int op)
{if (vis[op][x])return;vis[op][x] = 1;for (auto y : e[x])dfs(y, op);
}signed main()
{freopen("path.in", "r", stdin);freopen("path.out", "w", stdout);ios::sync_with_stdio(0);cin.tie(0);cin >> n >> m >> q >> type;for (int i = 1, u, v; i <= m; ++i){cin >> u >> v;e[u].emplace_back(v);}for (int i = 1; i <= n; ++i)sort(e[i].begin(), e[i].end());for (int i = 1; i <= n; ++i){memset(f[i], 0x3f, sizeof(f[i]));dfs(i, i);}// cerr << "!\n";for (int i = 1; i <= n; ++i)for (int j = 1; j <= n; ++j)if (vis[i][j])for (auto k : e[i])if (vis[k][j])f[j][i] = min(f[j][i], k);// cerr << "!\n";las = 1;int x, y, k;while (q--){cin >> x >> y >> k;if (type)x = (x ^ las) % n + 1, y = (y ^ las) % n + 1, k = (k ^ las) % n + 1;// cerr << "q=" << q << "!\n";if (!vis[x][y]){cout << "-1\n";// cerr << "x=" << x << " y=" << y << "\n";las = 1;continue;}top = 1, stk[1] = x;flag[x] = q + 1;// cerr << "x=" << x << " y=" << y << "\n";while (stk[top] != y) // 以终点为目标,从前往后压入点{int u = f[y][stk[top]];// cerr << "u=" << u << "\n";if (flag[u] == q + 1){top = 0;break;}stk[++top] = u;flag[u] = q + 1;}if (top < k)cout << "-1\n", las = 1;else{las = stk[k];cout << las << "\n";}}return 0;
}
t4
原题黑?
咕。