CSP2022 J2参考解析


题号为洛谷题号,直接搜索 2022CSP 即可。

P8813 [CSP-J2022] 乘方

  • 设计知识点:循环,快速幂

  • 题目描述很简单,求 \(a^b\),但是数据范围很大, \(a,b \in [1,10^9]\)

  • 方法1:一眼 long long 快速幂,可AC,但是有一个细节

  • 快速幂可能存在情况 ans 合理,ans*a 越界到 \([1,10^9]\),这个坑需要注意。

  • 方法2:细想一下 \(a=10^9, a^2=10^{18}\),其实直接暴力枚举就行。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10, INF = 0x3f3f3f3f, M = 1e9;

LL fpow(LL a, LL n) {
    LL ans = 1;
    while (n) {
        if (n & 1) ans = ans * a;
        if (ans > M || a > M) return -1;
        a = a * a;
        n >>= 1;
    }
    return ans;
}
LL slove2(LL a, LL n) {
    LL ans = 1;
    for (int i = 1; i <= n; i++) {
        ans = ans * a;
        if (ans > M) return -1;
    }
    return ans;
}
int main() {
    freopen("pow.in", "r", stdin);
    freopen("pow.out", "w", stdout);
    LL a, b;
    while (cin >> a >> b) {
        // cout << fpow(a, b) << endl;
        cout << slove2(a, b) << endl;
    }
    fclose(stdin), fclose(stdout);
    return 0;
}

P8814 [CSP-J2022] 解密

  • 设计知识点:数学,一元二次方程,二分

  • 题意:\(p*q=n, e*d=(p-1)(q-1)+1\),已知 \(n,e,d\),求 \(p,q\)

  • 方法1 数学推导

已知 \(pq=n, p+q=m\),求 \(p,q\)

\((p+q)^2=p^2+q^2+2pq\)

\((p-q)^2=p^2+q^2-2pq\)

两式相减,可得 \((p+q)^2 - (p-q)^2 = 4pq\),即 \(m^2 - (p-q)^2 = 4n\)

\(|p-q| = \sqrt{m^2-4n}\)

\(p+q=m,p≤q\)

\(p=\frac{m - \sqrt{m^2-4n}}{2},q=\frac{m+ \sqrt{m^2-4n}}{2}\)


  • 方法2:(其实就是方法1的归纳)明显可以用一元二次方程 \(O(1)\)

  • 一元二次方程:\(ax^2+bx+c=0\),已知 \(a,b,c\),求解 \(x\)

  • 公式:\(\Delta=b^2-4ac\)

  • 如果 \(\Delta < 0\),则无解。

  • 如果 \(\Delta=0\),则有唯一解 \(x=\frac{-b}{2a}\)

  • 如果 \(\Delta>0\),则有两个不同的解 \(x=\frac{-b±\sqrt{\Delta}}{2a}\)

  • 推导

\[\begin{aligned} &e*d = p*q - (p+q) + 2\\ &p + q = n-e*d+2 = m \\ &p*q = p * (m- p) = n, 结合上述方程,代入可得如下方程: \\ \\ &p^2 - m*p + n = 0 // 解此方程,求出 p \\ &\Delta = (-m)^2 - 4*1 *n = m^2 -4n \\ \\ &if(\Delta < 0) // NO 无解 \\ &else\{ \\ &\quad p = (m^2 ± \sqrt{\Delta})/2 \\ &\quad q = n/p \\ &\quad if(p > q) swap(p,q); \\ &\quad if(p*q==n \&\& p+q==m) // p q 就是答案 \\ &\quad else // NO 无解 \\ &\} \\ &\end{aligned} \]

  • 上述 \(O(1)\) 的说法其实不是那么准确,可以说是 \(O(\sqrt{n})\),主要在开方的时候,其复杂度一般是和牛顿迭代法相当的,比二分要优秀。
  • sqrt() 复杂度介绍

  • 方法3:二分答案 \(p\),单次复杂度 \(O(logn)\), 可以AC
  • \(p*q=n, p+q=m\)
  • 二分性质:两个正整数的和一定,那么差越小,乘积越大。

\[\begin{aligned} &证明:令 a+b = n, a-b = m,\\ &则 a = \frac{n+m}{2}, b=\frac{n-m}{2},\\ &a*b = \frac{(n+m)*(n-m)}{4} = \frac{n^2-m^2}{4} \\ &由于 n是固定的,所以 m越小,a*b越大。 \end{aligned} \]

image

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10, INF = 0x3f3f3f3f, M = 1e9;
LL k, n, e, d;

int main() {
    freopen("decode.in", "r", stdin);
    freopen("decode.out", "w", stdout);
    cin >> k;
    for (int i = 1; i <= k; i++) {
        cin >> n >> e >> d;
        LL m = n + 2 - e * d;
        LL delta = m * m - 4 * n;
        if (delta < 0) cout << "NO" << endl;
        else {
            LL p = (m - (LL)sqrt(delta)) / 2;
            LL q = (m + (LL)sqrt(delta)) / 2;
            // LL l = 1, r = m - 1, p, q;
            // while (l <= r) {
            //     p = l + r >> 1, q = m - p;
            //     if (p * q == n) break;
            //     else if (p * q < n) l = p + 1;
            //     else r = p - 1;
            // }
            // if (p > q) swap(p, q);
            if (p * q == n && p + q == m)
                cout << p << " " << q << endl;
            else cout << "NO" << endl;
        }
    }
    fclose(stdin), fclose(stdout);
    return 0;
}

P8815 [CSP-J2022] 逻辑表达式

  • 涉及知识点:大模拟,表达式树,栈
  • 这和表达式求值的题目比较相似,只是计算对象变为三元组,确定优先级
  • 另外就是计算的一些规则需要重新推导一下,如下图

image

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1e6+10,INF=0x3f3f3f3f, M=1e9;
unordered_map<char,int> pr {{'&', 2}, {'|', 1}};
/*
 (v,a,b)  = (value, and, or)

 (1,a1,b1) & (v,a2,b2) = (v, a1+a2, b1+b2)
 (0,a1,b1) | (v,a2,b2) = (v, a1+a2, b1+b2)
 (0,a1,b1) & (v,a2,b2) = (0, a1+1, b1)
 (1,a1,b1) | (v,a2,b2) = (1, a1, b1+1)
*/

struct T {
    int v,a,b;
} pa,pb;
stack<T> num;
stack<char> op;

void cal() {
    pb=num.top(), num.pop();
    pa=num.top(), num.pop();
    char c = op.top(); op.pop();
    int a1=pa.a, b1=pa.b, v=pb.v, a2=pb.a, b2=pb.b;
    if(c=='&') {
        // (0,a1,b1) & (v,a2,b2) = (0,a1+1,b1)
        // (1,a1,b1) & (v,a2,b2) = (v,a1+a2,b1+b2)
        if(pa.v==0) num.push({0,a1+1,b1});
        else num.push({v,a1+a2,b1+b2});
    } else if(c=='|') {
        // (0,a1,b1) | (v,a2,b2) = (v,a1+a2,b1+b2)
        // (1,a1,b1) | (v,a2,b2) = (1,a1,b1+1)
        if(pa.v==0) num.push({v,a1+a2,b1+b2});
        else num.push({1,a1,b1+1});
    }
}
int main() {
//    freopen("data.in", "r", stdin);
    string s;
    while(cin>>s) {
//        while(num.size()) num.pop();
//        while(op.size()) op.pop();

        for(int i=0; i<s.size(); i++) {
            char c = s[i];
            if(isdigit(c)) num.push({c-'0', 0, 0});
            else if(c=='(') {
                op.push(c);
            } else if(c==')') {
                while(op.top()!='(') cal();
                op.pop();
            } else {
                while(op.size() && op.top()!=')' &&
                        pr[op.top()] >= pr[c]) cal();
                op.push(c);
            }
        }
        while(op.size()) cal();
        cout<<num.top().v<<endl;
        cout<<num.top().a<<" "<<num.top().b<<endl;
    }
    fclose(stdin); fclose(stdout); return 0;
}

P8816 [CSP-J2022] 上升点列

  • 涉及知识点:最长上升子序列,二维DP
  • 状态:\(f_{i,j}\) 表示 以第 \(i\) 个元素结尾,插入 \(j\) 个元素的最长上升序列长度。
  • 转移:\(f_{i,j}=max\{f_{i,j-d}+d+1\} ,d = x2-x1+y2-y1-1\)
  • 目标:\(ans=max\{f_{i,k}\}\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 510, M = 110, INF = 0x3f3f3f3f;
int n, k, f[N][M], ans = 0;
struct T {
    int x, y;
    bool operator<(const T& rhs) const {
        if (x != rhs.x) return x < rhs.x;
        return y < rhs.y;
    }
} g[N];

// 状态:f[i][j] 表示序列以i结尾,添加j个点序列的最大长度
// 转移:f[i][j] = max(f[i][j], f[t][j-d]+d+1)
// d = x2-x1+y2-y1-1
int main() {
    freopen("point.in", "r", stdin);
    freopen("point.out", "w", stdout);
    cin >> n >> k;
    for (int i = 1, x, y; i <= n; i++) {
        cin >> x >> y, g[i] = {x, y};
    }
    sort(g + 1, g + 1 + n);
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= k; j++) {
            f[i][j] = j + 1;
            for (int t = 1; t < i; t++) {  // t -> i
                if (g[t].x > g[i].x || g[t].y > g[i].y) continue;
                int d = g[i].x - g[t].x + g[i].y - g[t].y - 1;
                if (j >= d) f[i][j] = max(f[i][j], f[t][j - d] + d + 1);
            }
            ans = max(ans, f[i][j]);
        }
    }
    cout << ans;
    fclose(stdin), fclose(stdout);
    return 0;
}
posted @ 2022-10-29 16:25  HelloHeBin  阅读(1305)  评论(0编辑  收藏  举报