Featured image of post Codeforces Round 1048 (Div. 2)

Codeforces Round 1048 (Div. 2)

C

题目链接

https://codeforces.com/contest/2139/problem/C

题目大意

一定数量的蛋糕(2^(k+1)),二人平分(分别叫a,b),现在有人想要特定数量的蛋糕,有两个操作,一个是a分一半给b,一个是b分一半给a,直到a拿到特定的x个,问要多少步,和具体方案

思路

刚开始两人是平分的,我们来假设测试分过几次后的状态,a有m个,b有2^(k+1)-m个,如果m小于总数的一半说明 上一个操作一定是a分一半,反之亦然。所以我们可以反向操作。目标a有的数量为x,如果小于总数的一半说明上一个 操作是1,否则是2。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define pii pair<int,int>
#define int long long

const int mod = 1e9+7;
const int inf = 0x3f3f3f;

void solve(){
    int k,x;
    cin >> k >> x;
    int s = 1LL << (k+1);
    int h = 1LL << k;
    int cur = x;
    vector<int> a;
    while(cur != h){
        if(cur < h){
            a.push_back(1);
            cur = cur * 2;
        }else{
            a.push_back(2);
            cur = cur * 2 - s;
        }
    }
    reverse(a.begin(), a.end());
    cout << a.size() << endl;
    for(int i = 0; i < (int)a.size(); i++){
        cout << a[i];
        if(i + 1 != (int)a.size()) cout << " ";
    }
    cout << endl;
}

signed main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);

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

总结

中题考查就是一个反向思考来简化问题

D

题目链接

https://codeforces.com/contest/2139/problem/D

题目大意

有一个排列数组,我们有两个操作让他们变得非递减。操作一可以将第i位和i+1位换位置,操作二将 i和i+2换位置且只能最多用一次。我们每次询问会给出左右边界,要我们返回在这个范围内的元素是否 用操作二爷不会减少操作次数,即总次数和只用操作一是一样的。

思路

我们那三个元素来进行分析,a,b,c(索引递增)。我们可以列出他们的大小关系,这里就不做证明,各位很容易就可以得到就是当a>b>c时,操作二会减少操作数量。
然后我们来分析abc是否一定要相邻,结论是不要。因为我们可以通过操作一让他们相邻。
那么我们要判断的就是被询问的左右边界是否包含了这样的三个数。方法也很简单,我们对于每一个i去先向前寻找一个大于a[i]的位置,设他为l,再向后寻找一个小于a[i]的位置设他为r,并存储在r位置对应的最大的l的位置(为什么要最大呢,因为往i后面继续遍历的时候可能找到同样的r但是l更大,此时记录更大的l这样后面的左右边界就更能包裹住)。

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;

#define endl '\n'
#define pii pair<int,int>
#define int long long

const int mod = 1e9+7;
const int inf = 0x3f3f3f;

void solve(){
    int n,q;
    cin >> n >> q;
    vector<int> a(n+1, 0),mxl(n+1, 0), mxr(n+1, 0),L(n+1, 0), prev(n+1, 0);
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++){
        mxl[i] = i - 1;
        while(mxl[i] > 0 && a[mxl[i]] < a[i]){
            mxl[i] = mxl[mxl[i]];
        }
    }
    for(int i = n; i >= 1; i--){
        mxr[i] = i + 1;
        while(mxr[i] <= n && a[mxr[i]] > a[i]){
            mxr[i] = mxr[mxr[i]];
        }
    }
    for(int i = 2; i < n; i++){
        if(mxl[i] > 0 && mxr[i] <= n){
            L[mxr[i]] = max(L[mxr[i]], mxl[i]);
        }
    }
    for(int i = 1; i <= n; i++)
        L[i] = max(L[i], L[i-1]);
    for(int i = 0; i < q; i++){
        int l, r;
        cin >> l >> r;
        if(l <= L[r]){
            cout << "NO" << endl;
        }else{
            cout << "YES" << endl;
        }
    }
}

signed main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);

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

总结

这道题开始确实想过用线段树,现在也不太明白这个思路是不是很偏,这个就是去寻找不符合的点然后看看边界有没有覆盖到他们,线段树先存好各个的状态再去查询,感觉思路差不多但是线段树比较多余对这道题来说。

使用 Hugo 构建
主题 StackJimmy 设计