JOI 2019 Final

A

统计后缀和

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
cs int N = 3e3 + 50;
int n, m; char mp[N][N];
int a[N][N], b[N][N];
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> n >> m;
	for(int i = 0; i < n; i++)
	scanf("%s", mp[i]);
	for(int i = 0; i < n; i++)
	for(int j = m - 1; ~j; j--)
	a[i][j] = a[i][j + 1] + (mp[i][j] == 'O');
	for(int i = n - 1; ~i; i--)
	for(int j = 0; j < m; j++)
	b[i][j] = b[i + 1][j] + (mp[i][j] == 'I');
	ll ans = 0; 
	for(int i = 0; i < n; i++)
	for(int j = 0; j < m; j++)
	if(mp[i][j] == 'J')
	ans += a[i][j] * b[i][j];
	cout << ans; return 0;  
}

B

贪心,先按 V i V_i Vi 排序,假设选了 k k k 个,那么一定会用最大的 k k k c i c_i ci
从后向前 D P \mathcal{DP} DP,求出到 i i i 最多选几个即可,复杂度 O ( n log ⁡ n ) \mathcal{O}(n\log n) O(nlogn)

#include<bits/stdc++.h>
#define cs const
#define pb push_back
#define fi first
#define se second
using namespace std;
cs int N = 1e5 + 50;
typedef pair<int, int> pi;
int n, m;
pi a[N]; int c[N];
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> n >> m;
	for(int i = 1; i <= n; i++)
	scanf("%d%d", &a[i].se, &a[i].fi);
	sort(a + 1, a + n + 1);
	for(int i = 1; i <= m; i++)
	scanf("%d", &c[i]);
	sort(c + 1, c + m + 1);
	int mx = 0; 
	for(int i = n; i; i--){
		int r = lower_bound(c + 1, c + m + 1, a[i].se) - c; 
		int z = min(mx + 1, m - r + 1);
		mx = max(mx, z);
	} cout << mx; return 0;
}

C

注意到相同颜色之间的顺序不会变
设计 d p i , j , k , 0 / 1 / 2 dp_{i,j,k,0/1/2} dpi,j,k,0/1/2 表示已经放了 i , j , k i,j,k i,j,k 个的逆序对数
复杂度 O ( n 3 ) \mathcal{O}(n^3) O(n3)

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int N = 405;
int n, p, q, r; 
char S[N]; int dp[N][N][N][3], sm[3][N], a[3][N];
void chkmn(int &a, int b){ if(a > b) a = b; }
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	scanf("%d%s", &n, S + 1);
	for(int i = 1; i <= n; i++){
		for(int j = 0; j < 3; j++)
		sm[j][i] = sm[j][i - 1];
		if(S[i] == 'R') a[0][++p] = i, ++sm[0][i]; 
		if(S[i] == 'G') a[1][++q] = i, ++sm[1][i];
		if(S[i] == 'Y') a[2][++r] = i, ++sm[2][i];
	}
	memset(dp, 0x3f, sizeof(dp));
	dp[0][0][0][0] = 
	dp[0][0][0][1] = 
	dp[0][0][0][2] = 0; 
	static int v[3], w[3];
	for(int i = 0; i <= p; i++)
	for(int j = 0; j <= q; j++)
	for(int k = 0; k <= r; k++){
		v[0] = i, v[1] = j, v[2] = k;
		for(int x = 0; x < 3; x++) if(v[x]){
			for(int z = 0; z < 3; z++)
			w[z] = v[z] - (x == z);
			int c = 0, vl = 1e9; 
			for(int z = 0; z < 3; z++) if(z != x){
				c += max(0, v[z] - sm[z][a[x][v[x]]]);
				vl = min(vl, dp[w[0]][w[1]][w[2]][z]);
			} if(vl < 1e9) dp[i][j][k][x] = vl + c;
		} 
	}	
	int ans = 1e9;
	for(int i = 0; i < 3; i++)
	chkmn(ans, dp[p][q][r][i]);
	ans = (ans == 1e9) ? -1 : ans;
	cout << ans << '\n';
	return 0; 
}

D

将每个点移到最近的点
统计出 c i , j c_{i,j} ci,j 表示还缺的个数
那么我们可以贪心从左边扫到右边,复杂度 O ( n ) \mathcal{O}(n) O(n)

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
typedef long long ll;
cs int N = 1e5 + 50;
int n, c[N][2]; ll ans; 
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> n; 
	for(int i = 1, x, y; i <= n + n; i++){
		scanf("%d%d", &x, &y); int _x, _y;
		if(y <= 1) ans += 1 - y, _y = 0;
		else ans += y - 2, _y = 1;
		if(x < 1) _x = 1, ans += 1 - x; 
		else if(x > n) _x = n, ans += x - n;
		else _x = x; ++c[_x][_y];
	}
	for(int i = 1; i <= n; i++)
	for(int j = 0; j < 2; j++)
	c[i][j] = 1 - c[i][j];
	for(int i = 1; i <= n; i++){
		if(c[i][0] > 0 && c[i][1] < 0){
			int d = min(c[i][0], -c[i][1]);
			c[i][0] -= d, c[i][1] += d, ans += d;  
		}
		if(c[i][0] < 0 && c[i][1] > 0){
			int d = min(-c[i][0], c[i][1]);
			c[i][0] += d, c[i][1] -= d, ans += d;
		} 
		ans += abs(c[i][0]) + abs(c[i][1]);
		c[i + 1][0] += c[i][0];
		c[i + 1][1] += c[i][1];
	} cout << ans; return 0; 
}

E

产生贡献的只能是 x x x 到直径的较远的一个端点的某一段
这启示我们从两个直径开始 d f s dfs dfs,统计每个点的贡献
用栈维护根到当前点产生贡献的点以及个数
向下走一步要将其余儿子中最深的覆盖的那些点 b a n ban ban
那么我们只需要先 d f s dfs dfs 重儿子,复杂度 O ( n ) \mathcal{O}(n) O(n)

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int N = 2e5 + 50;
int n, m, c[N];
vector<int> G[N];
int S, T, rt, mx;
void dfs(int u, int f, int d){
	if(d > mx) mx = d, rt = u;
	for(int v : G[u]) if(v != f)
		dfs(v, u, d + 1);
}
int dep[N], mxd[N], son[N], se[N], ans[N];
void pre_dfs(int u, int f){
	dep[u] = dep[f] + 1; 
	mxd[u] = se[u] = son[u] = 0; 
	for(int v : G[u]) if(v != f){
		pre_dfs(v, u);
		mxd[u] = max(mxd[u], mxd[v] + 1);
		if(mxd[son[u]] <= mxd[v]) son[u] = v; 
	} 
	for(int v : G[u]) if(v != f && v != son[u])
	se[u] = max(se[u], mxd[v] + 1);
}
int s[N], tp, bin[N], Sm;
void ins(int x){ Sm += !bin[x], ++bin[x]; }
void del(int x){ --bin[x], Sm -= !bin[x]; }
void sub_dfs(int u, int f){
	while(tp && dep[u] - dep[s[tp]] <= se[u]) 
		del(c[s[tp--]]);
	ins(c[s[++tp] = u]);
	if(son[u]) sub_dfs(son[u], u);
	while(tp && dep[u] - dep[s[tp]] <= mxd[u])
		del(c[s[tp--]]);
	ans[u] = max(ans[u], Sm);
	for(int v : G[u]) if(v != f && v != son[u]){
		if(!tp || s[tp] != u) 
		ins(c[s[++tp] = u]); sub_dfs(v, u);
	} if(tp && s[tp] == u) 
	del(c[s[tp--]]);
} 
int main(){
	#ifdef FSYo
	freopen("1.in", "r", stdin);
	#endif
	cin >> n >> m;
	for(int i = 1, u, v; i < n; i++)
	scanf("%d%d", &u, &v), 
	G[u].pb(v), G[v].pb(u);
	for(int i = 1; i <= n; i++)
	scanf("%d", &c[i]);
	dfs(1, 0, 0), S = rt; rt = mx = 0; 
	dfs(S, 0, 0), T = rt; 
	tp = 0, pre_dfs(S, 0), sub_dfs(S, 0);
	tp = 0, pre_dfs(T, 0), sub_dfs(T, 0);
	for(int i = 1; i <= n; i++)
	cout << ans[i] << '\n';
	return 0; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Joi 是一个用于 JavaScript 的强大的对象模型验证库。它可以用来验证和转换复杂的数据结构,如表单输入、API 请求和配置文件。Joi 提供了一组强大的验证规则和函数,可以轻松定义和应用对输入数据的验证逻辑。 Joi 的主要特点包括: 1. 可以通过链式调用来定义验证规则,使代码更加清晰和易读。 2. 支持各种类型的验证,包括字符串、数字、日期、枚举、数组、对象等。 3. 支持自定义验证规则和错误消息。 4. 提供丰富的验证函数,如必需字段、字符串长度、正则表达式匹配、数值范围、枚举值等。 5. 支持异步验证和自动转换。 6. 可以通过 `.validate()` 方法对数据进行验证,并返回验证结果。 以下是一个使用 Joi 进行表单验证的示例: ```javascript const Joi = require('joi'); // 定义验证规则 const schema = Joi.object({ username: Joi.string().alphanum().min(3).max(30).required(), password: Joi.string().pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).required(), email: Joi.string().email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }).required(), age: Joi.number().integer().min(18).max(99).required(), }); // 准备待验证的数据 const data = { username: 'john123', password: 'Password123', email: 'john@example.com', age: 25, }; // 进行验证 const result = schema.validate(data); // 输出验证结果 if (result.error) { console.log(result.error.details); } else { console.log('Validation passed'); } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值