Codeforces 訓練 Day1

Codeforces 訓練 Day1

Codeforces 1288 A Deadline

思路

觀察這個式子 x+dx+1x + \lceil{\frac{d}{x+1}}\rceil

讓其最小化,長得很像基本不等式,變形一下, min(x+1+dx+1)1n\min ( {x + 1 + \lceil \frac{d}{x + 1}}\rceil) - 1 \leq n

觀察一下,左邊的最小值會是, 2×d\lceil \sqrt{2 \times d}\rceil

之後就直接判斷就可

Codeforces 1288 B Yet Another Meme Problem

思路

a×b+a+b=conc(a,b)a \times b + a + b = conc(a, b)

而其中, conc(a,b)=a×10k+bconc(a, b) = a\times 10^k + bkk 爲某一確定的數)

由此可知, b=10k1b = 10^k - 1 ,而對於 aa 沒有限制

所以,只需統計滿足上述條件的 bb 即可

Codeforces 1288C Two Arrays

思路

\sout{這題有優秀得多的做法,參考洛谷大佬}

fi,j,kf_{i,j,k} 爲當前構造到第 ii 位,序列 a,ba,b 的最後一位分別爲 j,kj,k

由於序列 aa 單調不降,序列 bb 單調不升,這個狀態轉移非常好寫,同時由於單調性,fi,j,kf_{i,j,k}fi,j+1,kf_{i,j+1,k} 的轉移有很大一部分是相同的,所以只需要在轉移全部的 kk 時,對於每一個 j,kj,k 考慮在 j1,kj - 1,k 中沒有出現的部分,然後就是 fi,j1,kf_{i,j-1,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(ax,j,(ay,j))k\max (a_{x,j}, (a_{y,j})) \geq k

如果 ax,jka_{x,j} \geq kbx,j=1,otherwise  bx,j=0b_{x,j} = 1, otherwise \;b_{x,j}=0

於是問題轉化爲 是否存在兩行,其 bxby=2m1b_{x} \,|\, b_{y} = 2^m - 1

利用二進制壓位,同時做一個高位前綴和即可

#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

思路

考慮某個元素 xx,對於位置最小值,發現如果 xx 一直沒有被彈到最開始,則最小值就是其初始位置,否則被彈到開頭就是 11 (因爲一直不被彈,別的元素就會被彈到開頭,xx 的位置就至少會大於等於其初始位置)

現在解決最大值,我們發現對於被操作的數 xx 其可能的位置最大值一定是在被某次操作之前(可能操作很多次,這裏是討論每一次),因爲沒有被操作時,xx 的位置關於時間是不降的,所以我們就只需要對每次操作前的位置取 maxmax 即可

而對於沒有被操作的數,只需要在所有操作完成後,類似上面那樣即可

於是,我們需要實現,在某個位置上刪除一個數,添加一個數,查詢某個區間有多少個數,這裏直接用線段樹實現

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章