2025-11-21 hetao1733837的刷题记录
2025-11-21 hetao1733837的刷题记录
LG14415/LOJ3004 [JOISC 2015] Inheritance
原题链接1:[JOISC 2015] Inheritance
原题链接2:「JOISC 2015 Day 4」Inheritance
分析
呃……居然tag是二分!我们考虑单调性在哪……发现对于一条边,若其不为第$K$次操作的边权(比较小),则不为所有小于$K$操作的删边。考虑怎么$check$,模拟$Kruskal$的过程,通过$K$个并查集进行维护即可。这个思想是真的🐂🍺!
HE一下。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1005, M = 300005, K = 10005;
struct node{int a, b, c, id;
}e[M];
int fa[K][N], sz[K][N], ans[M];
bool cmp(node x, node y){return x.c > y.c;
}
int getfa(int k, int x){return x == fa[k][x] ? x : fa[k][x] = getfa(k, fa[k][x]);
}
void merge(int k, int x, int y){x = getfa(k, x);y = getfa(k, y);if (x == y)return ;if (sz[k][x] > sz[k][y])swap(x, y);fa[k][x] = y;sz[k][y] += sz[k][x];
}
int n, m, k;
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> m >> k;for (int i = 1; i <= m; i++){cin >> e[i].a >> e[i].b >> e[i].c;e[i].id = i;}sort(e + 1, e + m + 1, cmp);for (int i = 1; i <= n; i++){for (int j = 1; j <= k; j++){fa[j][i] = i;sz[j][i] = 1;}}for (int i = 1; i <= m; i++){int u = e[i].a, v = e[i].b, w = e[i].c, id = e[i].id;int l = 1, r = k; int pos = 0;while (l <= r){int mid = (l + r) >> 1;if (getfa(mid, u) != getfa(mid, v)){pos = mid;r = mid - 1;}else{l = mid + 1;}}ans[id] = pos;if (pos)merge(pos, u, v);}for (int i = 1; i <= m; i++)cout << ans[i] << '\n';
}
LG14412/JOI3001 [JOISC 2015] AAQQZ
原题链接1:
原题链接2:
分析
根本不会,mhh推了两篇题解,粘在这。
AT_joisc2015_h 题解 - Xttttr - 博客园
#6 2023.11.26 - ZSH_ZSH - 博客园
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 3005;
int n, c, a[N], cntl[N], cntr[N], dp[N][N], ans;
int calc(int l, int r, int lim){for (int i = 0; i <= c; i++)cntl[i] = cntr[i] = 0;if (l < 1 || l > n) return 0;cntl[a[l]]++;int L = l, suml = 1, sumr = 0, maxn = 0, cnt = 0;while (L > 1 && a[L - 1] >= a[L]){cntl[a[--L]]++;suml++;}int R = a[L];L = 0;for (int i = r; i <= n; i++){cntr[a[i]]++;if (a[i] < lim)return maxn;if (a[i] == lim)cnt++;else if (cntr[a[i]] > cntl[a[i]]){while (R > a[i] && R >= 0){suml -= cntl[R];R--;}} else{while (L <= c && cntl[L] <= cntr[L]){sumr += cntl[L];L++;}}int add = (L <= c) ? cntr[L] : 0;int len = min(sumr + add, suml);if (len + cnt == i - r + 1 && l >= len && l - len >= 0 && i + 1 <= n + 1)len += dp[l - len][i + 1];maxn = max(maxn, 2 * len + cnt);}return maxn;
}
void solve(){for (int i = 0; i <= n + 1; i++)for (int j = 0; j <= n + 1; j++)dp[i][j] = 0;for (int i = 1; i <= n; i++){for (int j = i; j <= n; j++){dp[i][j] = 0;if (a[i] == a[j])dp[i][j] = dp[i - 1][j + 1] + 1;}}for (int i = 1; i <= n; i++)ans = max(ans, 2 * dp[i][i] - 1 + calc(i - dp[i][i], i + dp[i][i], 0));for (int i = 1; i < n; i++)ans = max(ans, 2 * dp[i][i + 1] + calc(i - dp[i][i + 1], i + dp[i][i + 1] + 1, 0));for (int i = 1; i <= n; i++){int minn = 0;for (int j = i + 1; j <= n; j++){if (a[j] < a[i]) {minn = a[j];break;}}ans = max(ans, calc(i, i + 1, minn));}
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin >> n >> c;for (int i = 1; i <= n; i++){cin >> a[i];}solve();for (int i = 1; i <= n; i++)a[i] = c - a[i] + 1;reverse(a + 1, a + n + 1);solve();sort(a + 1, a + n + 1);int sum = 0;for (int i = 1; i <= n; i++){if (a[i] != a[i - 1]){ans = max(ans, sum);sum = 0;}sum++;}ans = max(ans, sum);cout << ans;return 0;
}
LG14413/LOJ [JOISC 2015] Card Game Is Great Fun
原题链接1:[JOISC 2015] Card Game Is Great Fun - 洛谷
原题链接2:「JOISC 2015 Day 3」Card Game Is Great Fun
分析
14点38分
$DP$是一眼的吧……但是我好像并不会设计状态和转移,稍考一会。
14点39分
想到fqh讲的一个trick,当时是矩阵长宽连边,此题我们难道可以花色点数连边?
那么,我们会建成一张图?这不是废话吗?咦,$V_i\ge 1$似乎可以贪心的$DP$一下?我在写什么?
那,建出图,我们找最长链即可?这不是蓝题吗?
但是,问题在于建图,这样是$O(n^2)$难道要运用另一个限制条件——只取第一或第三个?何意味?
14点50分
何意味啊?居然是高维$DP$!
既然限制了只能取第一或第三,那么,我们就把他计入状态!
即设$f_{i,j}$表示当前序列的第一个元素位置为$i$,第三个元素位置为$j$。但是,选第三个直接转移到$f_{i,j+1}$,但是选第一个没法转移,不知道$i$和$j$之后选哪个。
那么,多加一维$k$表示前三个元素怎么样?也不太行,因为不知道栈顶牌的具体信息。
那么,再加一维吧🤮,设$dp_{i,j,k,l}$表示上一次选择了原序列的第$l$张牌。选第一张即可转移到$f_{j,k,k+1,i}$,选第三张即可转移到$f_{i, j, k+1, k}$。显然,状态是 $O(n^4)$无法满足,/(ㄒoㄒ)/~~蓝题别搞!
发现$k=max(j,l)$,那不就省略一维了吗?
直接跑满!
然后呢,我想说,一些DP题实际上是记忆化搜索的手笔,所以,不会DP时,可以尝试写暴力,然后记忆化一下,说不定有客观的分数!
总结
限制扔进状态,观察数据范围大略估计状态维数,然后拿下!
正解
何意味,少开个long long卡我快20分钟,居然报TLE!
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 505;
int f[N][N][N];
int c[N], a[N], v[N];
int n;
bool check(int x, int y){if (!x || !y) //最开始和最结尾随便选 return true;if (c[x] == c[y] || a[x] == a[y])return true;return false;
}
int dfs(int i, int j, int k, int l){if (f[i][j][l] != -1){return f[i][j][l];}int ans = 0;if (i <= n && check(l ,i)){ans = max(ans, v[i] + dfs(j, k, k + 1, i));}if (k <= n && check(l ,k)){ans = max(ans, v[k] + dfs(i, j, k + 1, k));}f[i][j][l] = ans;return f[i][j][l];
}
int read(){char c = getchar();int x = 0, f = 1;while (c < '0' || c > '9'){if (c == '-')f = -1;c = getchar();}while (c >= '0' && c <= '9'){x = x * 10 + (c - '0');c = getchar();}return x * f;
}
signed main(){n = read();for (int i = 1; i <= n; i++){c[i] = read();a[i] = read();v[i] = read();}memset(f, -1, sizeof(f));printf("%lld", dfs(1, 2, 3, 0));
}
LG3615/LOJ2734 [JOISC 2016] Toilets
原题链接1:[JOISC 2016] 如厕计划
原题链接2:「JOISC 2016 Day 2」女装大佬
分析
记女生为+1,男生为-1,那么,发现每个男生需要"消耗"一个女生以保证两个厕所都不为空($2N$个人$N$个厕所,空依次就得崩)。那么,转化为后缀和,当后缀和小于$-1$时,就需要调整,然后吃吃吃,就做完了?何意味。
正解
#include <bits/stdc++.h>
using namespace std;
const int M = 100005;
int m;
long long n, ans = -1, k[M];
string s[M];
int main(){cin >> n;cin >> m;for (int i = 1; i <= m; i++){cin >> s[i] >> k[i];reverse(s[i].begin(), s[i].end());}long long tmp = 0;for (int i = m; i >= 1; i--){long long sum = 0, v = 1e9;for (int j = 0; j < s[i].length(); j++){if (s[i][j] == 'F')sum += 1;elsesum += -1;v = min(sum, v);}if (sum >= 0){ans = min(ans, tmp + v);tmp += sum * k[i];} else{tmp += sum * (k[i] - 1);ans = min(ans, tmp + v);tmp += sum;}}ans = -ans - 1LL;if (tmp < 0)cout << -1;elsecout << ans;
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/973221.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!