Codeforces 训练 Day1

Codeforces 训练 Day1

Codeforces 1288 A Deadline

思路

观察这个式子 x + ⌈ d x + 1 ⌉ x + \lceil{\frac{d}{x+1}}\rceil x+x+1d

让其最小化,长得很像基本不等式,变形一下, min ⁡ ( x + 1 + ⌈ d x + 1 ⌉ ) − 1 ≤ n \min ( {x + 1 + \lceil \frac{d}{x + 1}}\rceil) - 1 \leq n min(x+1+x+1d)1n

观察一下,左边的最小值会是, ⌈ 2 × d ⌉ \lceil \sqrt{2 \times d}\rceil 2×d

之后就直接判断就可

Codeforces 1288 B Yet Another Meme Problem

思路

a × b + a + b = c o n c ( a , b ) a \times b + a + b = conc(a, b) a×b+a+b=conc(a,b)

而其中, c o n c ( a , b ) = a × 1 0 k + b conc(a, b) = a\times 10^k + b conc(a,b)=a×10k+b k k k 为某一确定的数)

由此可知, b = 1 0 k − 1 b = 10^k - 1 b=10k1 ,而对于 a a a 没有限制

所以,只需统计满足上述条件的 b b b 即可

Codeforces 1288C Two Arrays

思路

这 题 有 优 秀 得 多 的 做 法 , 参 考 洛 谷 大 佬 \sout{这题有优秀得多的做法,参考洛谷大佬}

f i , j , k f_{i,j,k} fi,j,k 为当前构造到第 i i i 位,序列 a , b a,b a,b 的最后一位分别为 j , k j,k j,k

由于序列 a a a 单调不降,序列 b b b 单调不升,这个状态转移非常好写,同时由于单调性, f i , j , k f_{i,j,k} fi,j,k f i , j + 1 , k f_{i,j+1,k} fi,j+1,k 的转移有很大一部分是相同的,所以只需要在转移全部的 k k k 时,对于每一个 j , k j,k j,k 考虑在 j − 1 , k j - 1,k j1,k 中没有出现的部分,然后就是 f i , j − 1 , k f_{i,j-1,k} fi,j1,k 再加上没有出现的部分即可

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
#include <unordered_map>
#include <set>
#include <cmath>

using std :: vector;
using std :: swap;
using std :: queue;
using std :: set;
using std :: map;
using std :: string;
using std :: unordered_map;
using std :: priority_queue;

template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>Tp Abs(const Tp &a) {return a > 0 ? a : -a;}

template <typename Tp>void Read(Tp &x) {
    Tp in = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {in = in * 10 + ch - '0'; ch = getchar();}
    x = in * f;
}

const int SN = 1000 + 10;

const int SM = 10 + 2;

const int MOD = 1000000007;

typedef long long LL;

LL f[SM][SN][SN];

LL g[SN], h[SN];

int n, m;

int main(int argc, const char * argv[]) {
    
    Read(n), Read(m);
    
    for (int j = 1; j <= n; j++)
        for (int k = j; k <= n; k++)
            f[1][j][k] = 1;
    
    for (int i = 2; i <= m; i++) {
        
        for (int j = 1; j <= n; j++) {
            
            for (int k = j; k <= n; k++) g[k] = f[i - 1][j][k];
            
            for (int k = n - 1; k >= j; k--) g[k] = (g[k] + g[k + 1]) % MOD;
            
            for (int k = j; k <= n; k++) {
                
                f[i][j][k] = (f[i][j - 1][k] + g[k]) % MOD;
                
            }
            
        }
        
    }

    LL ans = 0;
        
    for (int j = 1; j <= n; j++) {
        for (int k = j; k <= n; k++)
            ans = (ans + f[m][j][k]) % MOD;
    }
    
    printf ("%lld\n", ans);
    
    return 0;
}

Codeforces 1288D Minimax Problem

思路

对于求最小值最大的问题,上来先二分

于是问题转化为,对于当前的数字矩阵,是否存在两行,其对应位置上的 max ⁡ ( a x , j , ( a y , j ) ) ≥ k \max (a_{x,j}, (a_{y,j})) \geq k max(ax,j,(ay,j))k

如果 a x , j ≥ k a_{x,j} \geq k ax,jk b x , j = 1 , o t h e r w i s e    b x , j = 0 b_{x,j} = 1, otherwise \;b_{x,j}=0 bx,j=1,otherwisebx,j=0

于是问题转化为 是否存在两行,其 b x   ∣   b y = 2 m − 1 b_{x} \,|\, b_{y} = 2^m - 1 bxby=2m1

利用二进制压位,同时做一个高位前缀和即可

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
#include <unordered_map>
#include <set>
#include <cmath>

using std :: vector;
using std :: swap;
using std :: queue;
using std :: set;
using std :: map;
using std :: string;
using std :: unordered_map;
using std :: priority_queue;

template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>Tp Abs(const Tp &a) {return a > 0 ? a : -a;}

template <typename Tp>void Read(Tp &x) {
    Tp in = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {in = in * 10 + ch - '0'; ch = getchar();}
    x = in * f;
}

const int SN = 300000 + 10;

const int SM = 8 + 2;

int a[SN][SM];

int vis[1 << 9];

int n, m, ansx, ansy;

bool check(int k) {
    
    memset(vis, 0, sizeof vis);
    
    int std = (1 << m) - 1;
    
    for (int i = 1; i <= n; i++) {
        
        int now = 0;
        
        for (int j = 0; j < m; j++)
            if (a[i][j] >= k) now |= (1 << j);
        
        vis[now] = i;
    }
    
    for (int sta = std; sta >= 0; sta--) {
            
        if (!vis[sta]) continue ;
        
        for (int j = 0; j < m; j++)
            
            if ((sta & (1 << j)) && (!vis[sta ^ (1 << j)]))
                vis[sta ^ (1 << j)] = vis[sta];
                    
    }
    
    for (int i = 1; i <= n; i++) {
        
        int now = 0;
        
        for (int j = 0; j < m; j++)
            if (a[i][j] >= k) now |= (1 << j);
        
        if (vis[std ^ now]) {
            ansx = i, ansy = vis[std ^ now];
            return true;
        }
        
    }
    
    return false;
}

int main(int argc, const char * argv[]) {
    
    Read(n), Read(m);
    
    for (int i = 1; i <= n; i++)
        for (int j = 0; j < m; j++)
            Read(a[i][j]);
    
    int l = 0, r = 1000000000, ans = 0;
    
    while (l <= r) {
        
        int mid = (l + r) >> 1;
        
        if (check(mid)) ans = mid, l = mid + 1;
        else r = mid - 1;
        
    }
    
    printf("%d %d\n", ansx, ansy);
    
    return 0;
}

Codeforces 1288E Messenger Simulator

思路

考虑某个元素 x x x,对于位置最小值,发现如果 x x x 一直没有被弹到最开始,则最小值就是其初始位置,否则被弹到开头就是 1 1 1 (因为一直不被弹,别的元素就会被弹到开头, x x x 的位置就至少会大于等于其初始位置)

现在解决最大值,我们发现对于被操作的数 x x x 其可能的位置最大值一定是在被某次操作之前(可能操作很多次,这里是讨论每一次),因为没有被操作时, x x x 的位置关于时间是不降的,所以我们就只需要对每次操作前的位置取 m a x max max 即可

而对于没有被操作的数,只需要在所有操作完成后,类似上面那样即可

于是,我们需要实现,在某个位置上删除一个数,添加一个数,查询某个区间有多少个数,这里直接用线段树实现

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
#include <unordered_map>
#include <set>
#include <cmath>

using std :: vector;
using std :: swap;
using std :: queue;
using std :: set;
using std :: map;
using std :: string;
using std :: unordered_map;
using std :: priority_queue;

template <typename Tp>Tp Max(const Tp &a, const Tp &b) {return a > b ? a : b;}
template <typename Tp>Tp Min(const Tp &a, const Tp &b) {return a < b ? a : b;}
template <typename Tp>Tp Abs(const Tp &a) {return a > 0 ? a : -a;}

template <typename Tp>void Read(Tp &x) {
    Tp in = 0, f = 1; char ch = getchar();
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') {in = in * 10 + ch - '0'; ch = getchar();}
    x = in * f;
}

const int SN = 300000 + 10;

const int SM = SN << 1;

int sum[SM << 2];

int pre[SN], next[SN], a[SN], pos[SN];

bool vis[SN];

int n, m;

void Modify(int x, int C, int l, int r, int rt) {
    
    if (l == r) {
        sum[rt] += C;
        return ;
    }
    
    int mid = (l + r) >> 1;
    
    if (x <= mid) Modify(x, C, l, mid, rt << 1);
    else Modify(x, C, mid + 1, r, rt << 1 | 1);
    
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
    
}

int Query(int QL, int QR, int l, int r, int rt) {
    
    if (QL <= l && QR >= r) return sum[rt];
    
    int mid = (l + r) >> 1, ans = 0;
    
    if (QL <= mid) ans += Query(QL, QR, l, mid, rt << 1);
    if (QR > mid) ans += Query(QL, QR, mid + 1, r, rt << 1 | 1);
    
    return ans;
}

int main(int argc, const char * argv[]) {
    
    Read(n), Read(m);
    
    for (int i = 1; i <= m; i++) Read(a[i]), vis[a[i]] = 1;
    
    for (int i = 1; i <= n; i++)
        if (vis[i]) pre[i] = 1, next[i] = i;
        else pre[i] = i, next[i] = i;
    
    for (int i = 1; i <= n; i++)
        Modify(i + m, 1, 1, n + m, 1), pos[i] = i + m;
    
    for (int i = 1; i <= m; i++) {
        
        int now = a[i];
        
        next[now] = Max(next[now], n - Query(pos[now] + 1, n + m, 1, n + m, 1));
        
        Modify(pos[now], -1, 1, n + m, 1);
        
        pos[now] = m - i + 1;
        
        Modify(pos[now], 1, 1, n + m, 1);
        
    }
    
    for (int i = 1; i <= n; i++)
        next[i] = Max(next[i], n - Query(pos[i] + 1, n + m, 1, n + m, 1));

    
    for (int i = 1; i <= n; i++)
        printf("%d %d\n", pre[i], next[i]);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值