牛客周赛 Round 100

A. 小红的双排列

小红很喜欢双排列

现在小红拿到了一个数 n ,请你帮他构造一个长度为 2×n 的双排列。
双排列:长度为 2×n 的双排列为两个长度为 n 的排列打乱顺序后得到的数组。

解题思路:

1...n/1...n 

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cout << i << " " << i;
        if (i < n)
            cout << " ";
    }
}

int main() {
    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

B. 小红的双排列拆分

小红拿到了一个长为 2×n 的双排列。现在他想从中取两个不相交子序列,使两个子序列都是长度为 n 的排列,请你帮帮他。

两个子序列是不相交的,当且仅当它们从原序列中取元素的位置没有重叠,即下标不同。

 解题思路:从长度为2*n的输入数据中取出两个长度为n的排列

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
    int n;
    cin >> n;
    vector<int> a(2 * n);
    for (int i = 0; i < 2 * n; i++) {
        cin >> a[i];
    }
    vector<int> vis(n + 1, 0);
    vector<int> s1, s2;
    for (int x : a) {
        if (!vis[x]) {
            s1.push_back(x);
            vis[x] = 1;
        } else {
            s2.push_back(x);
        }
    }
    for (int x : s1)
        cout << x << " ";
    cout << '\n';
    for (int x : s2)
        cout << x << " ";
    cout << '\n';
}
int main() {
    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

C.小红的双排列删除

小红拿到了一个长为 2×n 的双排列 {a1,a2,...,a2×n} 。

小芳能帮他进行任意次如下操作:

选择一个首尾元素相等的区间 [l,r](1≦l<r≦2×n; al=ar) ,将 al,al+1,...,ar​ 这段元素删除,并将其余元素按现有顺序拼接起来。

小红想知道,在可以进行任意次上述操作的情况下,能否将双排列中的所有数删除。

解题思路:

对于一个首尾相同的子区间是可以进行删除的

1 2 3 1 2 3
无法完全删除, 如果删除[1..1],会剩下[2,3] 
1 2 3 3 2 1

可以删除, [1,1]

由于每个数字都会出现两次, 只要[1..1]中有另外的数字, 那么就一定无法完全删除

因此我们可以模拟删除的过程,

先倒着遍历, 记录当前数字最后出现的位置

然后正向模拟删除的过程, 如果当前数字后续没有数字了, 那就删不掉了

输出"No" 

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int a[500050];
void solve() {
    int n;
    cin >> n;
//     vector<int> a(2 * n);
    int m=-1;
    for (int i = 0; i < 2 * n; i++){
        cin >> a[i];
    }
    map<int,int> mp;
    for(int i=2*n-1;i>=0;i--){
        if(mp[a[i]]==0) mp[a[i]]=i;
    }
    int p=0;
    for(int i=0;i<2*n;){
        if(mp[a[i]]>i){
            i=mp[a[i]]+1;
        }else{
            p=1;
            break;
        }
    }
    if(p) cout<<"No"<<'\n';
    else cout<<"Yes"<<'\n';
}
// 0 3
// 1 4
int main() {
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

 D.小红的双排列权值

对于给定的长度为 2×m 的双排列 {b1,b2,...,b2×m},对于每个 k,若其两次出现的位置为 l 和 r(1≦l<r≦2×m),则定义 f(k)=r−l−1.

小红将双排列的权值定义为每一对相同元素的下标距离之和,即:

f(i){i->(1..m)} 

现在小红拿到了一个长为 2×n 的双排列 {a1,a2,...,a2×n}
小芳可以帮他进行最多一次如下操作(也可以不操作):
选择下标 l,r (1≦l,r≦2×n), 交换 al,ar
请你帮小红求出可得到的最大权值。

解题思路:

让两个相同数字尽可能的离得远一些

对于交换操作

对于两个分离的子区间[1..1] [2...2] [3...3] 

如果交换了1和3

[1..3] [2...2] [1...3] 

交换后多出了两个中间区间

[L1,R1] [L2.R2]

swap(R1,L2)

由于只能swap一次, 所以我们就要找最左侧的R1,和最右侧的L2

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
    int n;
    cin >> n;
    vector<int> L(n + 1, n * 2);
    int L1 = 2 * n + 1, r = 0;
    int ans = 0, x;
    for (int i = 1; i <= n * 2; i++) {
        cin >> x;
        L[x] = min(L[x], i);
        if (L[x] < i) {
            ans += (i - L[x] - 1);
            L1 = min(L1, i);
        } else {
            r = max(r, L[x]);
        }
    }
    cout << ans + max(0LL, (ll)(r - L1) * 2) << '\n';
}
int main() {
    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

 E.小红的双排列删除得分

小红拿到了一个长为 2×n 的双排列 {a1,a2,...,a2×n} 。
小芳能帮他进行任意次如下操作:
选择一个首尾元素相等的区间 [l,r](1≦l<r≦2×n; al=ar) ,将 al​,al+1​,...,ar​ 这段元素删除,并将其余元素按现有顺序拼接起来,同时小红将获得 ∑i=lra分。 
请你帮小红求出可能的最高得分。 

解题思路: 

dp[i]: 前i个数所能得到的最高得分

只要当前数字的左端点存在就考虑删还是不删

不删的话dp[i]=dp[i-1]

删的话就是dp[i]=max(dp[i-1]+区间和)

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve() {
    ll n;
    cin >> n;
    vector<ll> a(n * 2 + 1), L(n + 1, n * 2), s(n * 2 + 1);
    for (ll i = 1; i <= n * 2; i++) {
        cin >> a[i];
        s[i] = s[i - 1] + a[i];
        L[a[i]] = min(L[a[i]], i);
    }
    vector<ll> dp(n * 2 + 1);
    for (ll r = 1; r <= n * 2; r++) {
        ll l = L[a[r]];
        dp[r] = dp[r - 1];
        if (l < r) {
            dp[r] = max(dp[r], dp[l - 1] + (s[r] - s[l - 1]));
        }
    }
    cout << dp[n * 2] << '\n';
}
int main() {
    int t = 1;
    // cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}

F.小红的双排列期望(easy)

小红拿到了一个长度为 2×n 的数组 {a1,a2,…,a2×n},初始所有元素都是 0,她可以进行任意次以下操作:
∙尝试使 ai​ 加 1。该操作有 bi 的概率成功。若成功,则 ai 加 1;若失败,则无任何变化。
小红希望最终 a 变成一个双排列。她希望最小化操作次数,请你求出小红最优策略下,最终操作次数的期望。答案对 109+7 取模。 

解题思路: 

从i-1到i的期望步数是1/p%

从0到i i/p%

大概率对应的大数,所以大概率尽量往后放

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
#define int long long
const int c = 1e9 + 7;
int q_p(int a, int b) {
    int ans = 1;
    a %= c;
    while (b) {
        if (b & 1)
            ans = ans * a % c;
        a = a * a % c;
        b >>= 1;
    }
    return ans % c;
}
void solve() {
    int n;
    cin >> n;
    vector<int> a(n * 2);
    for (int i = 0; i < n * 2; i++)
        cin >> a[i];
    sort(a.begin(), a.end());
    int ans = 0;
    int j = 100;
    for (int i = 0; i < n * 2; i++) {
        ans += j * q_p(a[i], c - 2) % c;
        ans %= c;
        if (i % 2)
            j += 100;
    }
    cout << ans << '\n';
}
signed main() {
    int t = 1; // cin>>t;
    while (t--){
        solve();
    }
    return 0;
}

G .小红的双排列期望(hard)

小红拿到了一个长度为 2×n 的数组 {a1,a2,…,a2×n},初始所有元素都是 000,她可以进行任意次以下操作:
,∙尝试使 ai 加 1。该操作有 bi% 的概率成功。若成功,则 ai加 1;若失败,如果 ai​ 大于 0 则会减 1(等于 0 则无变化)
小红希望最终 a 变成一个双排列。她希望最小化操作次数,请你求出小红最优策略下,最终操作次数的期望。答案对 109+7 取模。

解题思路: 

设f(k)为概率为 p%时,从k−1变为kk的操作次数期望
则f(k)=p%∗1+(1−p%)∗(1+f(k−1)+f(k))

p%的概率 走1步到达
(1−p%)的概率 走1步回到k−2
再走f(k−1)步到k−1
再走f(k)f步到k
化简得f(k)=[(1−p%)∗f(k−1)+1]/p%=[(100−p)∗f(k−1)+100]/p

对其再求前缀和 得到从0变为各个数的操作次数期望
概率只有100种可能性 可以直接全部预处理

#include <bits/stdc++.h>
using namespace std;
const int M = 1e9 + 7;
int b[200005];
long long dp[101][100005];
long long q_p(long long a, int k) {
    long long res = 1;
    a %= M;
    while (k > 0) {
        if (k & 1)
            res = res * a % M;
        a = a * a % M;
        b >>= 1;
    }
    return res % M;
}
void solve(){
    int n;
    cin >> n;
    for (int i = 1; i <= 100; i++) {
        for (int j = 1; j <= n; j++) {
            long long inv = q_p(i, M - 2);
            dp[i][j] = ((100 - i) * dp[i][j - 1] % M + 100) % M * inv % M;
        }
    }
    for (int i = 1; i <= 100; i++) {
        for (int j = 1; j <= n; j++) {
            dp[i][j] = (dp[i][j] + dp[i][j - 1]) % M;
        }
    }
    for (int i = 1; i <= n * 2; i++) {
        cin >> b[i];
    }
    sort(b + 1, b + 1 + n * 2);
    long long ans = 0;
    for (int i = 1; i <= n * 2; i++) {
        ans = (ans + dp[b[i]][(i + 1) / 2]) % M;
    }
    cout << ans << '\n';
}
int main(){
    int t = 1;
    while(t--){
        solve();
    }
    return 0;
}

 感谢大家的点赞和关注,你们的支持是我创作的动力!

 

### 关于周赛 Round 80 的相关信息 目前并未找到具体针对周赛 Round 80 的官方题解或比赛详情文档。然而,基于以往的比赛模式和惯例[^1],可以推测出此类赛事通常包含若干算法题目,覆盖基础数据结构动态规划、贪心策略以及图论等领域。 #### 可能涉及的内容范围 1. **签到题 (A 题)** 这类题目一般较为简单,旨在测试选手的基础编程能力。例如简单的数学计算或者字符串处理问题。 2. **中级难度题 (B 到 D 题)** 中级难度的题目往往需要一定的算法设计能力和复杂度分析技巧。比如: - 动态规划优化问题; - 贪心算法的应用场景; - 图遍历与最短路径求解; 3. **高阶挑战题 (E 或更高)** 对于更复杂的题目,则可能涉及到高级的数据结构操作(如线段树、并查集)、组合数学推导或者其他领域内的难题解决方法。 以下是根据过往经验给出的一个假设性的例子来展示如何解答类似的竞赛问题: ```python def solve_example_problem(n, m): """ 假设这是一个关于矩阵填充的问题, 给定 n 行 m 列大小的空间,按照某种规则填充值。 参数: n -- 矩阵行数 m -- 矩阵列数 返回值: result_matrix -- 完成后的二维列表形式的结果矩阵 """ # 初始化结果矩阵为全零状态 result_matrix = [[0]*m for _ in range(n)] value_to_fill = 1 direction_changes = [(0,1),(1,0),(0,-1),(-1,0)] # 方向变化顺序:右->下->左->上 current_direction_index = 0 row,col=0,0 while True: try: if not(0<=row<n and 0<=col<m): raise IndexError() if result_matrix[row][col]==0: result_matrix[row][col]=value_to_fill value_to_fill+=1 next_row,next_col=row+direction_changes[current_direction_index%len(direction_changes)][0],\ col+direction_changes[current_direction_index%len(direction_changes)][1] if any([not(0<=next_row<n), not(0<=next_col<m), bool(result_matrix[next_row][next_col])]): current_direction_index +=1 else: row,col=next_row,next_col except Exception as e: break return result_matrix if __name__ == "__main__": test_result=solve_example_problem(4,5) for line in test_result: print(line) ``` 上述代码片段展示了如何通过模拟实现一个螺旋状填充整数值至指定尺寸矩形中的过程作为示范案例之一[^4]。 ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值