2025-11-19~20 hetao1733837的record
2025-11-19~20 hetao1733837的record
11.19
LG14405/LOJ2994 [JOISC 2015] 复制粘贴 2
原题链接1:[JOISC 2015] 复制粘贴 2 / Copy and Paste 2
原题链接2:「JOISC 2015 Day1」复制粘贴 2
分析
链表?那为啥评绿?我要HE题解了。
对,应模拟是不对的,所以只考虑对答案的贡献。
难点就在于修改,也并非难吧。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int k, m;
string s;
struct node{
int a, b, c, len;
}inp[N];
int n;
int main(){
cin >> k >> m;
cin >> s;
s = " " + s;
cin >> n;
for (int i = n; i >= 1; i--){
cin >> inp[i].a >> inp[i].b >> inp[i].c;
inp[i].len = inp[i].b - inp[i].a;
}
for (int i = 1; i <= k; i++){
int cur = i;
for (int j = 1; j <= n; j++){
if (inp[j].c + 1 <= cur && cur <= inp[j].c + inp[j].len){
cur = inp[j].a + (cur - inp[j].c);
}
else if (inp[j].c + inp[j].len < cur){
cur -= inp[j].len;
}
}
cout << s[cur];
}
}
突然想打CF VP,那就来一场吧!
CF1307A Cow and Haybales
原题链接1:A
原题链接2:Cow and Haybales
分析
ber,我代码能力这么抽象?
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
int t;
int n, d;
int a[N];
int main(){
cin >> t;
while (t--){
cin >> n >> d;
for (int i = 1; i <= n; i++){
cin >> a[i];
}
int ans = a[1];
for (int i = 2; i <= n; i++){
if (a[i] > 0){
int dis = i - 1;
if (d < dis || d <= 0)
break;
ans += min(d / dis, a[i]);
d -= dis * min(d / dis, a[i]);
}
}
cout << ans << '\n';
}
}
CF1307B Cow and Friend
原题链接1:Cow and Friend
原题链接2:B - Codeforces
分析
呃……问题就出在,一方面可能先输的大于,后面有等于,导致WA;另一方面,对于maxn没有整完全,上下取整稍显抽象,还需多加练习。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int t, n, x;
int a[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> t;
while (t--){
cin >> n >> x;
int maxn = 0;
bool flag = false;
for (int i = 1; i <= n; i++){
cin >> a[i];
maxn = max(maxn, a[i]);
if (a[i] == x) {
flag = true;
}
}
if (flag) {
cout << 1 << '\n';
continue;
}
if (maxn > x){
cout << 2 << '\n';
continue;
}
int cnt = (x + maxn - 1) / maxn;
cout << cnt << '\n';
}
return 0;
}
CF1307C Cow and Message
原题链接1:Cow and Message
原题链接2:C - Codeforces
分析
看来我确实不能稳切黄……现在有两个想法,一个是枚举所有子集,炸到飞起来;另一个是枚举公差,也不太好办的样子……即使使用cnt数组的前缀和优化……看一眼题解吧!何意味?居然是子序列长度$\le 2$,好的,其实样例提示了,只不过做的是后缀和,统计区间各个字符出现次数。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005, M = 30;
string s;
int sum[N][M];
int ans[M][M];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> s;
int len = s.length();
s = " " + s;
for (int i = len; i >= 1; i--){
sum[i][s[i] - 'a']++;
for (int j = 0; j < 26; j++){
sum[i][j] += sum[i + 1][j];
}
}
for (int i = 1; i < len; i++){
for (int j = 0; j < 26; j++){
ans[s[i] - 'a'][j] += sum[i + 1][j];
}
}
long long res = 0;
for (int i = 0; i < 26; i++){
for (int j = 0; j < 26; j++){
res = max(res, ans[i][j]);
}
}
for (int i = 0; i < 26; i++){
res = max(res, sum[1][i]);
}
cout << res;
return 0;
}
11.20
LG14406/LOJ2995 [JOISC 2015] En-JOI-able Logo Design
原题链接1:LG14406 En-JOI-able Logo Design
原题链接2:2995「JOISC 2015 Day1」愉快的标志设计
分析
何意味……我赌他是贪心……或者我们设$dp_i$表示以$i$为起始的花费?那转移岂不是$O(n^2)$因为每次都要再跑一遍?何意味啊……
确实,答案的统计使用类似前缀和状物是较容易统计的……吗?所以,枚举起点即可。呃……我尝试一下。
好吧,我并非很会,但是这题引入了开两倍空间破环为链的Trick,不过我会。
正解
#include <bits/stdc++.h>
using namespace std;
const int N = (1 << 20) + 5;
int n, sj[N << 1], so[N << 1], si[N << 1];
string s;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
cin >> s;
int len = 1;
for (int i = 1; i <= n; i++)
len <<= 2;
s = s + s;
s = " " + s;
len <<= 1;
for (int i = 1; i <= len; i++){
sj[i] = sj[i - 1] + (s[i] == 'J');
so[i] = so[i - 1] + (s[i] == 'O');
si[i] = si[i - 1] + (s[i] == 'I');
}
int ans = len;
for (int i = len / 2; i < len; i++){
int res = 0;
for (int j = i - 1, k = 1; i - j + 1 <= len / 2; j -= 3 * k, k <<= 2){
res += k + si[j - k] - si[j];
res += k + so[j - k * 2] - so[j - k];
res += k + sj[j - k * 3] - sj[j - k * 2];
}
ans = min(ans, res);
}
cout << ans;
}
LG14407/LOJ2996 [JOISC 2015] Growing Vegetables is Fun 2
原题链接1:[JOISC 2015] Growing Vegetables is Fun 2
原题链接2:「JOISC 2015 Day1」有趣的家庭菜园 2
分析
好的,区别于LG14419的是这题会拔掉,且会获得一些额外的收益。
考虑如何设计$DP$状态。
设$cost_i$表示保证高度为$i$的IOI草能活下来所获得最大贡献,$f_i$表示$i$位置的IOI草能够存活的最大贡献。
考虑转移,
$f_i=\max\limits_{j=1}^{H_i}\left{ cost_j \right}+P_i$
$\forall j\in [1, H_i-1],cost_j\leftarrow cost_j - C_i$
$cost_j=\max(cost_j, f_i)$
同理向右求得$g_i$,的得出答案$ans=\max{f_i+g_i-P_i}$
但是,显然转移是$O(n^2)$的,考虑优化,注意到相当于区间最值,单点修改,直接上线段树。
何意味啊。
感觉这篇题解在乱叫。我在乱叫……虽然$H_i$很大,但是可以离散化,所以,题目结束。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 100005;
int n;
struct node{
int h, p, c;
}a[N];
int b[N];
int f[N], g[N];
struct segtree{
int mx[N << 2], tag[N << 2];
void pushup(int p){
mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
}
void maketag(int p, int v){
mx[p] += v;
tag[p] += v;
}
void down(int p){
if (!tag[p])
return ;
maketag(p << 1, tag[p]);
maketag(p << 1 | 1, tag[p]);
tag[p] = 0;
}
void modify(int p, int l, int r, int s, int t, int v){
if (s > t)
return ;
if (s <= l && r <= t){
maketag(p, v);
return ;
}
down(p);
int mid = (l + r) >> 1;
if (s <= mid)
modify(p << 1, l, mid, s, t, v);
if (t > mid)
modify(p << 1 | 1, mid + 1, r, s, t, v);
pushup(p);
}
void change(int p, int l, int r, int s, int v){
if (l == r){
mx[p] = max(mx[p], v);
return ;
}
down(p);
int mid = (l + r) >> 1;
if (s <= mid)
change(p << 1, l, mid, s, v);
else
change(p << 1 | 1, mid + 1, r, s, v);
pushup(p);
}
int query(int p, int l, int r, int s, int t){
if (s > t)
return 0;
if (s <= l && r <= t){
return mx[p];
}
down(p);
int mid = (l + r) >> 1;
int ans = 0xc0c0c0c0c0c0c0c0;
if (s <= mid)
ans = max(ans, query(p << 1, l, mid, s, t));
if (t > mid)
ans = max(ans, query(p << 1 | 1, mid + 1, r, s, t));
return ans;
}
}T1, T2;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++){
cin >> a[i].h >> a[i].p >> a[i].c;
b[i] = a[i].h;
}
sort(b + 1, b + n + 1);
int m = unique(b + 1, b + n + 1) - b - 1;
for (int i = 1; i <= n; i++){
a[i].h = lower_bound(b + 1, b + m + 1, a[i].h) - b;
}
for (int i = 1; i <= n; i++){
f[i] = T1.query(1, 1, m, 1, a[i].h) + a[i].p;
T1.modify(1, 1, m, 1, a[i].h - 1, -a[i].c);
T1.change(1, 1, m, a[i].h, f[i]);
}
for (int i = n; i >= 1; i--){
g[i] = T2.query(1, 1, m, 1, a[i].h) + a[i].p;
T2.modify(1, 1, m, 1, a[i].h - 1, -a[i].c);
T2.change(1, 1, m, a[i].h, g[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++){
ans = max(ans, f[i] + g[i] - a[i].p);
}
cout << ans;
}
LG14408/LOJ2997 [JOISC 2015] IOIOI Cards
原题链接1:[JOISC 2015]IOIOI Cards
原题链接2:「JOISC 2015 Day 1」卡片占卜
分析
好吧,转化为图论相当🐂🍺。可以证明,把区间变为全$I$是优的,因为若存在使得区间全$O$必存在全$I$且答案不劣于全$O$。我们得到了初始为$O$的四个端点,然后,每次修改相当于差分,区间修改。那么,对这些端点连边,使得其度数为奇数时,完成了反转。求最短路即可……似懂非懂。
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 500005;
int a[10];
vector<pair<int, int>> e[N];
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q;
int d[N];
bool vis[N];
int dijkstra(int S, int to){
memset(d, 0x3f, sizeof(d));
memset(vis, 0, sizeof(vis));
d[S] = 0;
q.push({0, S});
while (!q.empty()){
int u = q.top().second;
q.pop();
if (vis[u])
continue;
vis[u] = 1;
for (auto tmp : e[u]){
int v = tmp.first;
int w = tmp.second;
if (d[v] > d[u] + w){
d[v] = d[u] + w;
q.push({d[v], v});
}
}
}
return d[to];
}
int m;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int len = 0;
for (int i = 1; i <= 5; i++){
int x;
cin >> x;
len += x;
a[i] = len + 1;
}
cin >> m;
for (int i = 1; i <= m; i++){
int u, v;
cin >> u >> v;
e[u].push_back({v + 1, v - u + 1});
e[v + 1].push_back({u, v - u + 1});
}
int ans = min({dijkstra(a[1], a[2]) + dijkstra(a[3], a[4]),
dijkstra(a[1], a[3]) + dijkstra(a[2], a[4]),
dijkstra(a[1], a[4]) + dijkstra(a[2], a[3])});
if (ans >= 0x3f3f3f3f3f3f3f3f)
cout << -1;
else
cout << ans;
}
LG14409/LOJ2998 [JOISC 2015] Building 3
原题链接1:[JOISC 2015] Building 3
原题链接2:「JOISC 2015 Day2」Building 3
分析
何意味……排列$H$无疑成为了题目的突破口……真的吗?
统计前缀最大值,每次递增$0$或$1$,钦定一个数必须放在连续段末尾,算一下每个空位有多少种情况能放进去即可?
何意味……
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1000005;
int n, b[N];
signed main(){
cin >> n;
for (int i = 1; i < n; i++){
cin >> b[i];
}
int maxn = 0, cnt = 0, now = 0, pos = 0;
int ans = 0;
for (int i = 1; i < n; i++){
if (b[i] == maxn + 2){
now = b[i] - 2;
pos = i;
cnt++;
}
if (b[i] >= maxn + 3){
cout << 0;
return 0;
}
ans += maxn;
maxn = max(maxn, b[i]);
}
if (cnt == 1){
for (int i = 1; i < n; i++){
if (b[i] == now){
cout << pos - i;
return 0;
}
}
cout << 1;
return 0;
}
if (cnt > 1){
cout << 0;
return 0;
}
ans += maxn + 1;
cout << ans;
}
LG14410/LOJ2999 [JOISC 2015] Keys
原题链接1:[JOISC 2015] Keys - 洛谷
原题链接2:「JOISC 2015 Day2」Keys
分析
限制比较多,我们应该梳理一下。就是每位员工可能持有或没有钥匙,出公司都可以,但是进公司必须有钥匙或者门没锁,回公司时都可以锁门,只有有钥匙才能在离开公司时锁门。
难道是个模拟题?我看一眼$tag$,何意味?树形$DP$?如何建树成了大问题~
难道有没有钥匙+进出时间构成了祖先关系(错的)???woc,JOI的题确实好(但据说WQS二分过了,只能说 数据质量堪忧)!!!这个思维只能说🐂🍺
何意味啊……
🍬了一个小时,发现没有加上花花给的Hint的限制条件。
我们把所有的区间扔到一个数轴上,会发现所有$S_i$和$T_i$会组成一个个小区间。因为我们要使得关门时间最长,考虑对于这两个人什么情况下(🔑持有情况)可以使得区间贡献到关门时间中。
$$
\begin{cases}S\rightarrow T, 都要有 \S\rightarrow S, 前要有\T\rightarrow S, 都不必有\T\rightarrow T,后要有 \end{cases}
$$
对于都要有的,建边,然后,会出现$n$条链状物。对于只需要一把钥匙,存入点权,为了方便,我们直接把所有的链连成一个块,然后设$dp_{x,k,0/1}$表示考虑到第$x$个人,共分配了$k$把钥匙,$x$是否有钥匙的贡献最大值。
转移应该比较显然吧……可能我不会。然后,发现树上父子关系显然,再做一下滚动数组优化即可?
正解
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2005;
int n, m, k;
struct node{
char c;
int tim;
int id;
}a[N << 1];
int w[N << 1];
int top;
bool cmp(node x, node y){
return x.tim < y.tim;
}
int fa[N << 1], e[N << 1];
int f[2][N << 1][2];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> m >> k;
for (int i = 1; i <= n; i++){
int s, t;
cin >> s >> t;
a[++top] = {'S', s, i};
a[++top] = {'T', t, i};
}
sort(a + 1, a + top + 1, cmp);
int ans = a[1].tim + m - a[top].tim;
for (int i = 1; i < top; i++){
int val = a[i + 1].tim - a[i].tim;
int u = a[i].id, v = a[i + 1].id;
if (a[i].c == 'T' && a[i + 1].c == 'S'){
ans += val;
}
if (a[i].c == 'S' && a[i + 1].c == 'T'){
if (u == v)
w[u] += val;
else{
fa[v] = u;
e[u] += val;
}
}
if (a[i].c == 'S' && a[i + 1].c == 'S'){
w[u] += val;
}
if (a[i].c == 'T' && a[i + 1].c == 'T'){
w[v] += val;
}
}
int now = 0;
for (int i = 1; i <= n; i++){
if (!e[i]){
fa[now] = i;
while (fa[now]){
now = fa[now];
}
}
}
memset(f[0], 0xc0, sizeof(f[0]));
f[0][0][0] = ans;
now = 0;
for (int i = fa[0]; i; i = fa[i]){
now ^= 1;
memset(f[now], 0xc0, sizeof(f[now]));
for (int j = 0; j <= k; j++){
for (int t = 0; t < 2; t++){
f[now][j][0] = max(f[now][j][0], f[now ^ 1][j][t]);
if (j + 1 <= k)
f[now][j + 1][1] = max(f[now][j + 1][1], f[now ^ 1][j][t] + t * e[i] + w[i]);
}
}
}
cout << max(f[now][k][0], f[now][k][1]) << '\n';
}
posted on 2025-11-20 21:54 hetao1733837 阅读(0) 评论(0) 收藏 举报
浙公网安备 33010602011771号