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 - 博客园
正解
#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;
else
sum += -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;
else
cout << ans;
}
posted on 2025-11-22 16:28 hetao1733837 阅读(0) 评论(0) 收藏 举报
浙公网安备 33010602011771号