AtCoder Beginner Contest 140

传送门

A - Password

输出(n*n*n)即可。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 5;
int n;
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n;
    cout << n * n * n;
}

B - Buffet

简单模拟。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 25;
int n;
int a[N], b[N], c[N];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> b[i];
    for(int i = 1; i < n; i++) cin >> c[i];
    int ans = 0;
    a[0] = -2;
    for(int i = 1; i <= n; i++) {
        ans += b[a[i]];
        if(a[i] == a[i - 1] + 1) ans += c[a[i - 1]];
    }
    cout << ans;
}

C - Maximal Value

题意:
现在有(n)个数,每个数为(a_i),但你现在并不知道(a)
现在给出(n-1)个数(b_i),满足(b_igeq max(a_i,a_{i+1})),现在问(sum {a_i})最大的可能值为多少。

思路:
我们可以先只对两位分情况讨论一下:

  • 假设现在有(b_ileq b_{i+1}),那么显然(a_{i+1})只能为(b_i),要让和最大,那么(a_i=b_i,a_{i+2}=b_{i+1})
  • (b_i>b_{i+1}),那么有(a_{i+1}=b_{i+1},a_i=b_i,a_{i+2}=b_{i+1})
  • 注意到不论哪种情况,都有(a_i=b_i,a_{i+2}=b_{i+1},a_{i+1})为两者的最小值。

所以直接这样来搞就行了。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 105;
int n;
int a[N], b[N];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n;
    for(int i = 1; i < n; i++) cin >> b[i];
    int ans = 0;
    for(int i = 2; i < n; i++) {
        ans += min(b[i - 1], b[i]);
    }
    ans += b[1] + b[n - 1];
    cout << ans;
}

D - Face Produces Unhappiness

题意:
现在给出一个只含(L,R)的序列,现在定义(good pos)为:假设当前为(L),其左边也为(L);或者当前为(R),其右边也为(R)
现在你可以进行至多(k)次操作,每次操作可以选择一段区间将其翻转,注意是区间翻转(180)度,朝向和位置都会改变。
现在问最多可以有多少个(goodpos)

思路:

  • 注意到每次翻转一段区间([l,r]),最多会多出(2)(goodpos)
  • 为什么?
  • 首先我们肯定会选择类似于这样的区间(R[Lcdots L]R)或者(L[Rcdots R]L)这样的才能最优,不然你翻转一下有啥用?
  • 并且又因为区间里面不会产生新的(goodpos),所以分析一下边界就行啦。
  • 也就是说每次会多两个(goodpos),会不会出现只多(1)个的情况呢?
  • 肯定会的,这时肯定翻转一次就全都一样的,所以最终答案就为(min(n-1,now+2*k))

实现起来还是很简单的~

Code
#include <bits/stdc++.h>
#define MP make_pair
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;

int n, k;
char s[N];

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> k;
    cin >> s + 1;
    int ans = 0;
    for(int i = 1; i <= n; i++) {
        if(s[i] == s[i - 1] && s[i] == 'L') ++ans;
    }
    for(int i = n; i >= 1; i--) {
        if(s[i] == s[i + 1] && s[i] == 'R') ++ans;
    }
    ans = min(n - 1, ans + 2 * k);
    cout << ans;
}

E - Second Sum

题意:
定义(X_{L,R})表示区间([L,R])中第二大的数。
现在给出一个(1)~(n)的排列,求:

[sum_{L=1}^{N-1}sum_{R=L+1}^N X_{L,R} ]

思路:

  • 显然我们可以枚举每个数,并将其作为区间第二大数,然后看有多少个区间满足条件。
  • 然后我一开始写了个线段树(T)了...
  • 其实并不用,因为每次我们只需要用上比它大的数,所以我们从大的数往小搞,每次将其(pos)插入一个(set)里面就行了。
  • 之后直接在(set)里面二分,因为比它大的数的(pos)是具有单调性的。
  • 当我们找到区间右边比它大的两个位置和左边比它大的两个位置之后,直接统计答案即可。

细节见代码:

Code
#include <bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e5 + 5;

int n;
vector <pii> v;

multiset <int> s;

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n;
    for(int i = 1; i <= n; i++) {
        int x; cin >> x;
        v.push_back(MP(x, i));
    }
    s.insert(0), s.insert(0);
    s.insert(n + 1), s.insert(n + 1);
    sort(v.begin(), v.end());
    reverse(v.begin(), v.end());
    ll ans = 0;
    for(auto i : v) {
        int num = i.fi, p = i.se;
        auto it = s.lower_bound(p);
        int R1 = *it++;
        int R2 = *it--;
        --it;
        int L1 = *it--;
        int L2 = *it;
        if(L1) ans += 1ll * num * (L1 - L2) * (R1 - p);
        if(R1 <= n) ans += 1ll * num * (p - L1) * (R2 - R1);
        s.insert(p);
    }
    cout << ans;
    return 0;
}

F - Many Slimes

感觉这是一个很简单的贪心模拟题?
(vector+set)模拟一下繁衍的过程即可,每次最大的数肯定产生比它小的最大的数,所以在(vector)中依次把数取出来,然后直接在(set)里面查找即可。
一开始写了个(O(n))利用指针模拟的写法,但有两个点(wa)了...痛苦。

Code

原文地址:https://www.cnblogs.com/heyuhhh/p/11491647.html