UVa 1618 Weak Key

题目描述

哲洙是 ICPC\texttt{ICPC}ICPC(国际密码程序公司)的密码学家。最近,他开发了一种名为 ACM\texttt{ACM}ACM(高级加密方法)的密码算法。ACM\texttt{ACM}ACM 使用一个密钥来加密消息,加密后的消息称为密文。在 ACM\texttt{ACM}ACM 中,要解密密文需要使用与加密时相同的密钥。

英熙是 ICPC\texttt{ICPC}ICPC 的密码分析师,她发现 ACM\texttt{ACM}ACM 算法存在弱密钥的问题。当使用弱密钥加密消息时,无需密钥就能轻易从密文中恢复消息。

ACM\texttt{ACM}ACM 使用一个互不相同的正整数序列 (N1,N2,…,Nk)(N_1, N_2, \ldots, N_k)(N1,N2,,Nk) 作为密钥。弱密钥具有以下特殊模式:

在密钥中存在四个位置 p<q<r<sp < q < r < sp<q<r<s,使得满足以下两种模式之一:

  1. Nq>Ns>Np>NrN_q > N_s > N_p > N_rNq>Ns>Np>Nr
  2. Nq<Ns<Np<NrN_q < N_s < N_p < N_rNq<Ns<Np<Nr

例如,密钥 (10,30,60,40,20,50)(10, 30, 60, 40, 20, 50)(10,30,60,40,20,50) 具有模式 (1)(1)(1):取 p=1,q=2,r=4,s=5p=1, q=2, r=4, s=5p=1,q=2,r=4,s=5,则 Nq=60>Ns=50>Np=30>Nr=20N_q=60 > N_s=50 > N_p=30 > N_r=20Nq=60>Ns=50>Np=30>Nr=20,所以该密钥是弱密钥。

现在需要编写一个程序,对于给定的密钥序列,判断它是否为弱密钥。

输入格式

输入包含 TTT 个测试用例。每个测试用例包含两行:

  • 第一行:整数 kkk (4≤k≤5000)(4 \leq k \leq 5000)(4k5000),表示密钥长度
  • 第二行:kkk 个互不相同的正整数 (1≤Ni≤100,000)(1 \leq N_i \leq 100,000)(1Ni100,000)

输出格式

对于每个测试用例,输出一行:如果是弱密钥输出 YES\texttt{YES}YES,否则输出 NO\texttt{NO}NO

题目分析

问题理解

我们需要在序列中寻找四个位置 p<q<r<sp < q < r < sp<q<r<s,满足以下两种不等式关系之一:

  • 模式 1Nq>Ns>Np>NrN_q > N_s > N_p > N_rNq>Ns>Np>Nr
  • 模式 2Nq<Ns<Np<NrN_q < N_s < N_p < N_rNq<Ns<Np<Nr

这两个模式可以理解为在序列中存在一个特定的"波浪形"或"锯齿形"关系。

暴力解法分析

最直接的解法是使用四重循环枚举所有可能的 p,q,r,sp, q, r, sp,q,r,s 组合:

for (int p = 0; p < n; p++)
  for (int q = p+1; q < n; q++)
    for (int r = q+1; r < n; r++)
      for (int s = r+1; s < n; s++)
        if (检查模式1或模式2) return true;

这种方法的时间复杂度为 O(k4)O(k^4)O(k4),对于 k=5000k = 5000k=5000 来说完全不可行。

优化思路

我们可以重新组织搜索策略,将时间复杂度降低到 O(k3)O(k^3)O(k3)

  1. 固定 qqqsss:对于模式 1,我们固定 qqqsss,其中 q<s−1q < s-1q<s1(保证中间有位置给 rrr
  2. 寻找 ppp:在 qqq 的左边寻找满足 Np<NsN_p < N_sNp<Ns 的最大值作为候选 ppp
  3. 寻找 rrr:在 qqqsss 之间寻找满足 Nr<NpN_r < N_pNr<Np 的值

对于模式 2 采用类似的思路,但不等式关系相反。

算法步骤

模式 111 检查 (Nq>Ns>Np>NrN_q > N_s > N_p > N_rNq>Ns>Np>Nr)

111. 遍历所有可能的 qqq (1≤q≤k−3)(1 \leq q \leq k-3)(1qk3)
222. 对于每个 qqq,遍历所有可能的 sss (q+2≤s≤k−1)(q+2 \leq s \leq k-1)(q+2sk1)
333. 如果 Nq>NsN_q > N_sNq>Ns

  • qqq 的左边找到小于 NsN_sNs 的最大值作为候选 ppp
  • qqqsss 之间检查是否存在 rrr 使得 Nr<NpN_r < N_pNr<Np
模式 222 检查 (Nq<Ns<Np<NrN_q < N_s < N_p < N_rNq<Ns<Np<Nr)

111. 遍历所有可能的 qqq (1≤q≤k−3)(1 \leq q \leq k-3)(1qk3)
222. 对于每个 qqq,遍历所有可能的 sss (q+2≤s≤k−1)(q+2 \leq s \leq k-1)(q+2sk1)
333. 如果 Nq<NsN_q < N_sNq<Ns

  • qqq 的左边找到大于 NsN_sNs 的最小值作为候选 ppp
  • qqqsss 之间检查是否存在 rrr 使得 Nr>NpN_r > N_pNr>Np

复杂度分析

  • 外层循环:O(k)O(k)O(k)
  • 内层循环:O(k)O(k)O(k)
  • 内部查找:O(k)O(k)O(k)
  • 总复杂度:O(k3)O(k^3)O(k3)

虽然 O(k3)O(k^3)O(k3) 在最坏情况下对于 k=5000k=5000k=5000 仍然较大,但由于实际数据可能不会达到最坏情况,且算法实现简单高效,这个解法在实际评测中能够通过。

解题代码

// Weak Key
// UVa ID: 1618
// Verdict: Accepted
// Submission Date: 2025-11-28
// UVa Run Time: 0.360s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

const int INF = 1e9;

bool isWeakKey(const vector<int>& arr) {
    int n = arr.size();
    
    // 模式1: N_q > N_s > N_p > N_r
    for (int q = 1; q < n - 2; q++) {
        // 预处理:对于每个可能的s,快速判断是否存在满足条件的p和r
        int maxLeft = -INF;  // q左边最大值
        for (int p = 0; p < q; p++) {
            maxLeft = max(maxLeft, arr[p]);
        }
        
        // 对于每个s > q+1,如果arr[s] < arr[q]
        // 我们需要找到p使得arr[p] < arr[s],且存在r在q和s之间使得arr[r] < arr[p]
        for (int s = q + 2; s < n; s++) {
            if (arr[s] < arr[q]) {
                // 在q左边找到小于arr[s]的最大值作为候选p
                int candidateP = -INF;
                for (int p = 0; p < q; p++) {
                    if (arr[p] < arr[s] && arr[p] > candidateP) {
                        candidateP = arr[p];
                    }
                }
                
                if (candidateP != -INF) {
                    // 检查q和s之间是否存在r使得arr[r] < candidateP
                    int minBetween = INF;
                    for (int r = q + 1; r < s; r++) {
                        minBetween = min(minBetween, arr[r]);
                    }
                    if (minBetween < candidateP) {
                        return true;
                    }
                }
            }
        }
    }
    
    // 模式2: N_q < N_s < N_p < N_r
    for (int q = 1; q < n - 2; q++) {
        int minLeft = INF;  // q左边最小值
        for (int p = 0; p < q; p++) {
            minLeft = min(minLeft, arr[p]);
        }
        
        for (int s = q + 2; s < n; s++) {
            if (arr[s] > arr[q]) {
                // 在q左边找到大于arr[s]的最小值作为候选p
                int candidateP = INF;
                for (int p = 0; p < q; p++) {
                    if (arr[p] > arr[s] && arr[p] < candidateP) {
                        candidateP = arr[p];
                    }
                }
                
                if (candidateP != INF) {
                    // 检查q和s之间是否存在r使得arr[r] > candidateP
                    int maxBetween = -INF;
                    for (int r = q + 1; r < s; r++) {
                        maxBetween = max(maxBetween, arr[r]);
                    }
                    if (maxBetween > candidateP) {
                        return true;
                    }
                }
            }
        }
    }
    
    return false;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int testCases;
    cin >> testCases;
    
    while (testCases--) {
        int k;
        cin >> k;
        vector<int> key(k);
        for (int i = 0; i < k; i++) cin >> key[i];
        
        if (isWeakKey(key)) cout << "YES\n";
        else cout << "NO\n";
    }
    
    return 0;
}

另外一种实现:

// Weak Key
// UVa ID: 1618
// Verdict: Accepted
// Submission Date: 2025-11-28
// UVa Run Time: 0.240s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

const int INF = 1e9;

bool isWeakKey(const vector<int>& arr) {
    int n = arr.size();
    
    // 预处理每个区间的最小值和最大值
    vector<vector<int>> minInRange(n, vector<int>(n, INF));
    vector<vector<int>> maxInRange(n, vector<int>(n, -INF));
    
    for (int i = 0; i < n; i++) {
        minInRange[i][i] = maxInRange[i][i] = arr[i];
        for (int j = i + 1; j < n; j++) {
            minInRange[i][j] = min(minInRange[i][j - 1], arr[j]);
            maxInRange[i][j] = max(maxInRange[i][j - 1], arr[j]);
        }
    }
    
    // 预处理每个位置左边的有序数组
    vector<multiset<int>> leftSets(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < i; j++) {
            leftSets[i].insert(arr[j]);
        }
    }
    
    // 检查两种模式
    for (int q = 1; q < n - 2; q++) {
        for (int s = q + 2; s < n; s++) {
            // 模式1: N_q > N_s > N_p > N_r
            if (arr[q] > arr[s]) {
                auto it = leftSets[q].lower_bound(arr[s]);
                if (it != leftSets[q].begin()) {
                    it--;
                    int candidateP = *it;
                    if (minInRange[q + 1][s - 1] < candidateP) {
                        return true;
                    }
                }
            }
            // 模式2: N_q < N_s < N_p < N_r
            else if (arr[q] < arr[s]) {
                auto it = leftSets[q].upper_bound(arr[s]);
                if (it != leftSets[q].end()) {
                    int candidateP = *it;
                    if (maxInRange[q + 1][s - 1] > candidateP) {
                        return true;
                    }
                }
            }
        }
    }
    
    return false;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    
    int testCases;
    cin >> testCases;
    
    while (testCases--) {
        int k;
        cin >> k;
        vector<int> key(k);
        for (int i = 0; i < k; i++) cin >> key[i];
        
        cout << (isWeakKey(key) ? "YES" : "NO") << endl;
    }
    
    return 0;
}

总结

本题的关键在于理解弱密钥的两种模式特征,并通过合理的搜索策略将时间复杂度从不可行的 O(k4)O(k^4)O(k4) 优化到可接受的 O(k3)O(k^3)O(k3)。算法通过固定中间两个位置 qqqsss,然后在限定范围内寻找满足条件的 ppprrr,有效地减少了不必要的计算。

这种"固定中间,寻找两边"的思路在解决类似的序列模式匹配问题时非常有用,值得学习和掌握。

<think> 我们遇到了"EE certificate key too weak"错误,这通常在使用libcurl时发生,意味着服务器端证书的密钥强度不足(例如使用1024位RSA密钥,而现代标准要求至少2048位)。以下是如何解决: ### 问题原因 1. **服务器证书密钥过弱**:服务器使用的SSL/TLS证书密钥长度低于安全标准(如1024位RSA)。 2. **libcurl的安全限制**:新版本libcurl默认要求更强的密钥强度(例如2048位以上)。 ### 解决方案 #### 方案1:降低安全要求(不推荐,仅用于测试) 通过设置`CURLOPT_SSL_OPTIONS`选项禁用密钥强度检查: ```c curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST | CURLSSLOPT_NO_REVOKE); // 添加允许弱密钥的选项 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_WEAK_SERVER_CERT); ``` #### 方案2:更新服务器证书(根本解决办法) 联系服务器管理员升级证书,使用更强密钥(如2048位或4096位RSA,或ECDSA密钥)。 #### 方案3:自定义证书验证(高级) 如果必须连接且无法更新证书,可以自定义证书验证回调: ```c // 自定义证书验证回调函数 static int cert_verify_callback(X509_STORE_CTX *ctx, void *arg) { // 这里可以跳过某些验证,但强烈不推荐 return 1; // 总是返回1表示验证通过(危险!) } // 在curl配置中设置 curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_function); static CURLcode ssl_ctx_function(CURL *curl, void *ssl_ctx, void *userptr) { SSL_CTX *ctx = (SSL_CTX *)ssl_ctx; SSL_CTX_set_cert_verify_callback(ctx, cert_verify_callback, NULL); return CURLE_OK; } ``` ### 完整示例代码(方案1) ```c #include <curl/curl.h> int main(void) { CURL *curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); // 允许弱证书(仅用于测试环境!) curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_WEAK_SERVER_CERT); // 也可以同时设置其他SSL选项(可选) // curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_WEAK_SERVER_CERT | CURLSSLOPT_NO_REVOKE); CURLcode res = curl_easy_perform(curl); if(res != CURLE_OK) fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); curl_easy_cleanup(curl); } return 0; } ``` ### 安全警告 - `CURLSSLOPT_ALLOW_WEAK_SERVER_CERT` 会降低连接安全性,**切勿在生产环境使用**。 - 长期解决方案必须是升级服务器证书。 ### 其他注意事项 1. **libcurl版本**:此选项要求 libcurl 7.73.0 或更新版本。旧版本可能需要其他方法。 2. **错误代码**:此错误对应 `CURLE_SSL_CIPHER` (35) 或 `CURLE_PEER_FAILED_VERIFICATION` (51)。 §§相关问题§§ 1. 如何检查服务器证书的密钥长度? 2. libcurl中有哪些其他CURLOPT_SSL_OPTIONS选项可用? 3. 如何在不降低安全性的情况下绕过证书验证问题? 4. 如何为libcurl指定自定义CA证书包? </think> 当使用 libcurl 时遇到 "EE certificate key too weak" 错误,这表示服务器证书的密钥强度不足(通常是因为使用弱于 2048 位的 RSA 密钥)。以下是完整的解决方案: ### 问题原因分析 1. **密钥强度不足**:服务器使用弱密钥(如 1024 位 RSA),而现代安全标准要求至少 2048 位 2. **libcurl 安全策略**:新版本 libcurl (7.73.0+) 默认拒绝弱证书 3. **OpenSSL 安全级别**:底层 OpenSSL 库的安全级别设置过高 ### 解决方案 #### 方法 1:降低安全级别(仅限测试环境) 在开发/测试环境中,可通过配置 OpenSSL 降低安全级别: ```c #include <curl/curl.h> #include <openssl/ssl.h> static CURLcode ssl_ctx_function(CURL *curl, void *ssl_ctx, void *userptr) { SSL_CTX *ctx = (SSL_CTX *)ssl_ctx; // 设置安全级别为 1(允许1024位密钥) SSL_CTX_set_security_level(ctx, 1); return CURLE_OK; } int main() { CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "https://your-weak-cert-site.com"); // 设置SSL上下文回调 curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, ssl_ctx_function); // 执行请求... } ``` #### 方法 2:使用 libcurl 的弱证书选项(推荐临时方案) ```c curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_WEAK_SERVER_CERT); ``` #### 方法 3:完全禁用证书验证(极度危险,仅用于测试) ```c curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 不验证对等证书 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // 不验证主机名 ``` ### 永久解决方案 1. **升级服务器证书**: - 要求服务器管理员更换为 2048 位或更强的 RSA 密钥 - 或使用 ECDSA 证书(更安全且性能更好) 2. **更新客户端信任库**: ```c // 指定自定义CA证书包 curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/custom-cacert.pem"); ``` 3. **降级 libcurl 版本**(不推荐): ```bash # 安装旧版本(7.72.0 之前) apt-get install libcurl4-openssl-dev=7.71.1-1 ``` ### 完整示例代码 ```c #include <curl/curl.h> int main() { CURL *curl; CURLcode res; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com"); // 解决方案1:允许弱证书 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_WEAK_SERVER_CERT); // 解决方案2:禁用证书验证(危险!) // curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // 解决方案3:使用自定义CA包 // curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/cacert.pem"); res = curl_easy_perform(curl); if(res != CURLE_OK) fprintf(stderr, "请求失败: %s\n", curl_easy_strerror(res)); curl_easy_cleanup(curl); } curl_global_cleanup(); return 0; } ``` ### 安全建议 1. **生产环境必须使用强证书**,临时解决方案仅限测试 2. 定期更新 CA 证书包: ```bash curl https://curl.se/ca/cacert.pem -o /path/to/cacert.pem ``` 3. 使用证书固定技术增强安全: ```c curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, "sha256//YOUR_PUBKEY_HASH"); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值