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)    收藏  举报

导航