【题解】CSP-J2022复赛题解

CSP-J2022第二轮题解

T1.乘方 ⊗ \otimes

简化题意:给定 a , b a,b a,b,求 a b a^b ab 的值。若 a b > 1 0 9 a^b>10^9 ab>109,则输出 -1

  • a , b ⩽ 1 0 9 a,b\leqslant10^9 a,b109

看似需要快速幂,其实不然。当 a > 1 a\gt 1 a>1 时,其实暴力乘的话乘不了几次的。也就是说,特判一下,如果 a = 1 a=1 a=1,则输出 1,否则直接暴力乘起来。

T2.解密 ⊗ \otimes

题意:给定一个正整数 k k k,有 k k k 次询问,每次给定三个正整数 n i , e i , d i n_i, e_i, d_i ni,ei,di,求两个正整数 p i , q i p_i, q_i pi,qi,使 n i = p i × q i n_i = p_i \times q_i ni=pi×qi e i × d i = ( p i − 1 ) ( q i − 1 ) + 2 e_i \times d_i = (p_i - 1)(q_i - 1) + 2 ei×di=(pi1)(qi1)+2

非常明显的推式子。 e i × d i = p i ⋅ q i − p i − q i + 2 e_i\times d_i=p_i\cdot q_i-p_i-q_i+2 ei×di=piqipiqi+2 ∵ n i = p i ⋅ q i \because n_i=p_i\cdot q_i ni=piqi ∴ e i × d i = n − p i − q i + 1 \therefore e_i\times d_i=n-p_i-q_i+1 ei×di=npiqi+1,即 p i + q i = n + 2 − e i × d i p_i+q_i=n+2-e_i\times d_i pi+qi=n+2ei×di

现有 { p i + q i = n + 2 − e i × d i p i × q i = n \begin{cases}p_i+q_i=n+2-e_i\times d_i\\ p_i\times q_i=n \end{cases} {pi+qi=n+2ei×dipi×qi=n,可以令 q i = n p i , m = n + 2 − e i × d i q_i=\frac{n}{p_i},m=n+2-e_i\times d_i qi=pin,m=n+2ei×di,则 p i + n p i = m p_i+\frac{n}{p_i}=m pi+pin=m,经整理得 p i 2 − m ⋅ p i + n = 0 {p_i}^2-m\cdot p_i+n=0 pi2mpi+n=0,用一元二次方程的方法求就好了。 Δ = m 2 − 4 n \Delta=m^2-4n Δ=m24n,则 p i = − b ± Δ 2 a p_i=\frac{-b\pm\sqrt\Delta}{2a} pi=2ab±Δ ,最后再验证一下,同样较为简单。

T3.逻辑表达式 ⊗ \otimes

又是逻辑运算……~~不知道为什么同样的东西 CCF 能在16道题中出现两次。~~这题还好,不是特别的难。主要是先看一下样例,思考一下自己是怎么运算的。这一步就先略过,主要讲最终的思路。我们倒着想,考虑一下结果是怎么产生的。显然,结果是一个类似于 x op y 的式子产生的。这时候可以分类讨论:如果是 1 | x,那就产生了一次 | 短路;如果是 0 & x,那就产生了一次 & 短路;否则这一次操作就没有短路。现在,我们就要延伸下去了。就拿 x op y 来看,在计算 xy 的过程中,肯定也会产生一些短路,所以,接下来就要考虑 xy 是怎么样得来的。这就是一个递归的过程。接下来,就要干的一件事情就是:通过给定的表达式 S,找到这个表达式做的最后一个运算,进而将表达式拆分为两部分进行运算。如何找到表达式的最后一个运算呢?就要从题目中说的优先级讲起。首先,括号里的表达式可以直接忽略。其次,找到最后一个或运算。【原因:与运算比或运算优先】如果没有或运算,就选择最后一个与运算。到这里,我们就可以写出一份伪代码:

countAnd = 0, countOr = 0
func solve(l, r):
    if str countain '|':
        leftPart = solve(l, pos - 1)
        if leftPart = 1: //统计短路次数
            countOr++, return 1
        return solve(pos + 1, r)
    if str countain '&': //此时表达式中没有'|'运算了
        leftPart = solve(1, pos - 1)
        if leftPart = 0: //统计短路次数
            countAnd++, return 0
        return solve(pos + 1, r)
    if str is likes "(xxx)":
        return solve(l + 1, r - 1)
    return 1 or 0

到这里,代码基本就写好了。如果仔细观察一下伪代码,会发现它的复杂度依然是 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn),因为寻找最后一个与/或运算还需要扫一遍。非常显然这种东西需要预处理,预处理的方法也很简单,在没有括号的情况下直接就能记录,有了括号之后用栈简单的维护一下就好了。

T4.上升点列 ⊗ \otimes

首先,假如说把 K K K 个备用点单独排成一行,那么答案就是 K K K。怎么做到优化呢?就是依靠题目中给定的点,有了这些点之后,就会节省一些备用点。如果说这 K K K 个备用点可以将 r e s res res 个点串在一起,那么答案就是 K + r e s K+res K+res,这道题的核心就是求出这个 r e s res res

今年的题目好像还差一个动态规划没有出,就考虑动态规划吧。动态规划题从问题出发。问题:在 n n n 个点中,加入 k k k 个点所能达到的最长长度。那我们就可以得出状态: f i , j f_{i,j} fi,j 表示前 i i i 个点中加入 j j j 个点所能达到的最大长度。但是这个状态明显和点的顺序有关。我们要规定一种点的顺序。先来考虑转移方程在确定点的顺序吧。考虑 f i , j f_{i,j} fi,j f i − 1 , j f_{i-1,j} fi1,j 之间的差异:第 i i i 个点。第 i i i 个点可以与 1 ∼ i − 1 1\sim i-1 1i1 中的点相连,暂且假设它与 p p p 点相连,可以得到点 i i i 与点 p p p 相连所需要的备用点数 d = x i − x p + y i − y p d=x_i-x_p+y_i-y_p d=xixp+yiyp。那么可以得出: f i , j = max ⁡ k = 1 i − 1 ( f k , d i s i , k + d i s i , k + 1 ) ⋅ [ d i s i , k ⩽ j ] f_{i,j}=\max_{k=1}^{i-1}(f_{k,dis_{i,k}}+dis_{i,k}+1)\cdot[dis_{i,k}\leqslant j] fi,j=maxk=1i1(fk,disi,k+disi,k+1)[disi,kj],具体见代码。

代码

T1.乘方

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
signed main(){
	int a = read(), b = read();
	if(a == 1){
		cout << 1 << endl;
		return 0;
	}
	int res = 1;
	for(int i = 1; i <= b; i++){
		res *= a;
		if(res > 1e9) {
			cout << "-1" << endl;
			return 0;
		}
	} 
	cout << res << endl;
	return 0;
}

T2.解密

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= (ch == '-' ? -1 : 1), ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = s * 10 + ch - '0', ch = getchar());
	return s * w;
}
//545 1 499
signed main(){
//	freopen("decode.in", "r", stdin);
//	freopen("decode.out", "w", stdout);
	int T = read();
	while(T--){
		int n = read(), b1 = read(), b2 = read();
		int m = n + 2 - b1 * b2;
		int delta = m * m - 4 * n;
		if(delta < 0){
			cout << "NO" << endl;
			continue;
		}
		int sqrt_delta = sqrt(delta) + 0.05;
		int p1 = (m + sqrt_delta) >> 1;
		int p2 = (m - sqrt_delta) >> 1;
		int q1 = n / p1;
		int q2 = n / p2;
		if(p1 + q1 == m && p1 * q1 == n){
			cout << min(p1, q1) << " " << max(p1, q1) << endl;
//			printf("%lld+%lld=%lld,%lld*%lld=%lld", p1, q1, m, p1, q1, n);
		} else if (p2 + q2 == m && p2 * q2 == n){
			cout << min(p2, q2) << " " << max(p2, q2) << endl;
//			printf("%lld+%lld=%lld,%lld*%lld=%lld", p2, q2, m, p2, q2, n);
		} else {
			cout << "NO" << endl;
		}
	}
	return 0;
}

T3.逻辑表达式

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= ch == '-' ? -1 : 1, ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = 10 * s + ch - '0', ch = getchar());
	return s * w;
}
const int MAXN = 1000005;
int cnt_and, cnt_or, lastand[MAXN], lastor[MAXN], resand[MAXN], resor[MAXN];
string s;
int solve(int l, int r){
	if(resor[r] >= l){
		int res = solve(l, resor[r] - 1);
		if(res == 1) {
			cnt_or++;
			return 1;
		}
		return solve(resor[r] + 1, r);
	}
	if(resand[r] >= l){
		int res = solve(l, resand[r] - 1);
		if(res == 0){
			cnt_and++;
			return 0;
		}
		return solve(resand[r] + 1, r);
	}
	if(s[l] == '(' && s[r] == ')') return solve(l + 1, r - 1);
	return (s[l] - '0');
}
signed main(){
	cin >> s;
	int n = s.size();
	s = " " + s;
	int top = 0;
	for(int i = 1; i <= n; i++){
		if(s[i] == '&') lastand[top] = i;
		else if (s[i] == '|') lastor[top] = i;
		else if (s[i] == '(') top++;
		else if (s[i] == ')') lastand[top] = lastor[top] = 0, top--;
		resand[i] = lastand[top], resor[i] = lastor[top];
	}
	int res = solve(1, n);
	cout << res << endl;
	cout << cnt_and << " " << cnt_or << endl;
	return 0;
}

T4.上升点列

#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int s = 0, w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; w *= (ch == '-' ? -1 : 1), ch = getchar());
	for(; ch >= '0' && ch <= '9'; s = s * 10 + ch - '0', ch = getchar());
	return s * w;
}
const int MAXN = 505;
const int MAXK = 105;
struct Node{
	int x, y;
	bool operator < (const Node &rhs) const {
		if(x == rhs.x) return y < rhs.y;
		return x < rhs.x;
	}
} a[MAXN];
int f[MAXN][MAXK];
signed main(){
	int n = read(), K = read();
	for(int i = 1; i <= n; i++){
		a[i].x = read(), a[i].y = read();
	}
	sort(a + 1, a + 1 + n);
//	for(int i = 1; i <= n; i++){
//		printf("(%lld,%lld)\n", a[i].x, a[i].y);
//	}
	for(int i = 1; i <= n; i++){
		f[i][0] = 1;
	}
	for(int i = 1; i <= n; i++){
		for(int j = 1, cnt; j < i; j++){
			if(a[i].y < a[j].y) continue;
			cnt = a[i].x - a[j].x + a[i].y - a[j].y - 1;
			if(cnt > K){
//				cout << "Too long" << endl;
				continue;
			}
//			printf("%lld(%lld,%lld) %lld(%lld,%lld) %lld\n", j, a[j].x, a[j].y, i, a[i].x, a[i].y, cnt);
			for(int k = cnt; k <= K; k++){
				f[i][k] = max(f[i][k], f[j][k - cnt] + cnt + 1);
			}
		}
	}
	int ans = -1;
	for(int i = 1; i <= n; i++){
		for(int j = 0; j <= K; j++){
			ans = max(ans, f[i][j] + K - j);
		}
	}
	cout << ans << endl;
	return 0;
}

结语

考前做了很多预测,都实现了。比如第三题比第四题难、会考到与或非逻辑运算之类的。这一次我的成绩也还不错,整体挺顺利的。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值