Codeforces Global Round 21 A

论坛 期权论坛 金融     
5oym   2022-6-26 21:20   6927   8
A和B无讲解
A NIT orz!

代码实现

#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n, z;
        cin >> n >> z;
        int res = 0;
        for(int i = 1; i <= n; i++){
            int x;
            cin >> x;
            res = max(res, x | z);
        }
        cout << res << '\n';
    }

}
B NIT Destroys the Universe

#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 5;
int a[maxn];

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n, cnt0 = 0;
        cin >> n;
        for(int i = 1; i <= n; i++){
            int x;
            cin >> x;
            cnt0 += (x == 0);
            a = x;
        }
        if (cnt0 == n) cout << 0 << '\n';
        else{
            for(int i = 1; i <= n; i++){
                if (!a) continue;
                int j = i;
                while(j + 1 <= n && a[j + 1]) j++;
                if (j - i + 1 + cnt0 == n) cout << 1 << '\n';
                else cout << 2 << '\n';
                break;
            }
        }
    }

}
C Fishingprince Plays With Array

题意

给出两个序列和一个数,每次可以把序列中一个的倍数拆成,或者把连续个相同的数变成一个数字,问能否通过操作使得相同
分析

我们把所有能拆的数字全部拆开,然后比较两个序列是否相等即可.注意由于可能拆出的段非常多,所以要把值相同的若干个值缩成一个区间.
代码实现

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n, m, k;
        cin >> n >> m;
        vector<pair<LL, LL> > a, b;
        for(int i = 1; i <= n; i++){
            LL x;
            cin >> x;
            LL cnt = 1;
            while(x % m == 0) cnt *= m, x /= m;
            if (a.empty() || a.back().first != x) a.push_back({x, cnt});
            else a.back().second += cnt;
        }
        cin >> k;
        for(int i = 1; i <= k; i++){
            LL x;
            cin >> x;
            LL cnt = 1;
            while(x % m == 0) cnt *= m, x /= m;
            if (b.empty() || b.back().first != x) b.push_back({x, cnt});
            else b.back().second += cnt;
        }
        cout << (a == b ? "Yes" : "No") << '\n';
    }

}
D Permutation Graph

题意

给出一个排列,如果满足分别是区间内的最小值和最大值,则之间有边,问从号点走到号点至少需要多少步.
分析

从样例给的图来看似乎到了每个点之后贪心地走向最远的点一定是更优的(但是我不太会证明).我们当然希望每次尽可能跨过尽可能长的区间.
我的做法是这样的,分治去做,用线段树或者ST表预处理之后每次求得当前区间的最大值和最小值位置分别为(如果就交换一下位置),然后我们指定其中一条路径就是,然后递归区间.
这样为什么是对的呢,因为我们发现我们求出后,左边的路径是不可能连到右边的点的,因为是最小值或者最大值,如果路径区间跨过了,那么一定有一个端点不是最值(因为一定是一个最值),同理,右边的路径也不可能连到左边的点.所以直接从连到一定是最优的.
最多递归层,每层长度为,总时间复杂度为(线段树)或者(ST表)
代码实现

#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int maxn = 2.5e5 + 5, INF = 0x3f3f3f3f;
int a[maxn], pos[maxn];

struct Node{
    int l, r, minv, maxv;
}tr[maxn * 4];
int n, m, p;

void pushup(int u){
    tr.minv = min(tr[u << 1].minv, tr[u << 1 | 1].minv);
    tr.maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
}

void build(int u, int l, int r){
    tr.l = l, tr.r = r;
    if (l == r){
        tr.minv = tr.maxv = a[r];
        return;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

int query_min(int u, int l, int r){
    if (tr.l >= l && tr.r <= r) return tr.minv;
    int mid = tr.l + tr.r >> 1;
    int v = INF;
    if (l <= mid) v = query_min(u << 1, l, r);
    if (r > mid) v = min(v, query_min(u << 1 | 1, l, r));
    return v;
}

int query_max(int u, int l, int r){
    if (tr.l >= l && tr.r <= r) return tr.maxv;
    int mid = tr.l + tr.r >> 1;
    int v = 0;
    if (l <= mid) v = query_max(u << 1, l, r);
    if (r > mid) v = max(v, query_max(u << 1 | 1, l, r));
    return v;
}

int solve(int l, int r){
    if (l + 1 == r) return 1;
    if (l >= r) return 0;
    int maxv = query_max(1, l, r);
    int minv = query_min(1, l, r);
    int L = pos[maxv], R = pos[minv];
    if (L > R) swap(L, R);
    return solve(l, L) + 1 + solve(R, r);
}

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    int T;
    cin >> T;
    while(T--){
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> a, pos[a] = i;
        if (n == 1){
            cout << 0 << '\n';
            continue;
        }
        build(1, 1, n);
        cout << solve(1, n) << '\n';
    }

}
E Placing Jinas

题意

给出一个不增序列,满足的格子是白色的.初始在有一个玩偶,每次操作可以删除上的一个玩偶,然后在各生成一个玩偶,问想要白色区域没有任何玩偶至少需要多少次操作.
分析

首先我们打个表观察一下如果所有都相等每个点需要移动多少次.

我们发现第第行第列需要移动的次数为(如果对组合数比较敏感的话应该是能发现的),我们要求每个点的移动次数其实就是求一个矩形框内的和.比如样例中要求的是这个矩阵的和.

然后我们可以发现(应该也可以公式推导吧...)左上角在的矩形的和等于这个矩形右下角的数字减一(蓝色圈出的数,打表发现的).所以我们可以求一个矩形内需要的总步数.
但是实际上是不相等的,但是我们可以根据的值把原图形拆成一个一个小矩形,然后利用前缀和的性质求解每个矩形对答案的贡献,就可以求得答案,复杂度是
代码实现

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn = 1e6 + 5, mod = 1e9 + 7;
int a[maxn];
LL fact[maxn], invfact[maxn];

LL qpow(LL a, LL b, LL mod)
{
    LL res = 1;
    while (b)
    {
        if (b & 1) res = 1LL * res * a % mod;
        a = 1LL * a * a % mod;
        b >>= 1;
    }
    return res;
}

void init(){
    fact[0] = invfact[0] = 1;
    for(int i = 1; i < maxn; i++) fact = (LL)fact[i - 1] * i % mod;
    invfact[maxn - 1] = qpow(fact[maxn - 1], mod - 2, mod);
    for(int i = maxn - 2; i; i--)
        invfact = (LL)invfact[i + 1] * (i + 1) % mod;       
}

inline LL C(int a, int b){
    if (a < b) return 0;
    return fact[a] * invfact % mod * invfact[a - b] % mod;
}

int main(){

    cin.tie(0);
    cout.tie(0);
    ios::sync_with_stdio(0);

    init();
    int n;
    cin >> n;
    n++;
    for(int i = 1; i <= n; i++) cin >> a;
    while(n && !a[n]) n--;
    if (!n){
        cout << 0 << '\n';
        return 0;
    }
    vector<pair<int, int> > p;
    for(int i = 1; i <= n; i++){
        int j = i;
        while(j + 1 <= n && a[j + 1] == a) j++;
        p.push_back({i, j});
        i = j;
    }
    LL res = 0;
    for(auto [l, r] : p)
        res = (res + C(r + a[l], a[l]) - C(l + a[l] - 1, a[l]) + mod) % mod;
    cout << res << '\n';

}
本文使用 Zhihu On VSCode 创作并发布
分享到 :
0 人收藏

8 个回复

倒序浏览
2#
吴宇  管理员  伦敦金丝雀码头交易员 | 2022-6-26 21:21:35 发帖IP地址来自 北京
厉害
3#
dawang  1级新秀 | 2022-6-26 21:22:29 发帖IP地址来自 中国
d题可以o(n)做,代码量小一些
4#
uaygq  1级新秀 | 2022-6-26 21:22:47 发帖IP地址来自 山西晋中
D题证明没看懂,可以细讲一下吗[衰]
5#
uhzk1  1级新秀 | 2022-6-26 21:23:28 发帖IP地址来自 中国
哪里没懂呢?
6#
xaqu  1级新秀 | 2022-6-26 21:23:41 发帖IP地址来自 北京
[可怜][可怜][可怜]
7#
28y3e3  1级新秀 | 2022-6-26 21:24:14 发帖IP地址来自 北京
我这个就是笨办法了[捂脸]
8#
wvpsv  1级新秀 | 2022-6-26 21:24:49 发帖IP地址来自 福建
懂了,我发现我不是证明没有看懂,而是没有想到这个贪心和分治的思路[爱]
9#
7x_2vj  1级新秀 | 2022-6-26 21:25:41 发帖IP地址来自 北京
[可怜]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:
帖子:
精华:
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP