1. 程式人生 > 實用技巧 >[Codeforces]Round 657 div2題解(A-D)

[Codeforces]Round 657 div2題解(A-D)

Before the Beginning

轉載請將本段放在文章開頭顯眼處,如有二次創作請標明。
原文連結:https://www.codein.icu/cf1379/

前言

狀態奇差,而且題目太毒瘤了,於是 B + A,rk 1600 滾去掉分。
賽後10min切C,1h切D……
這次一改 Codeforces 一貫的結論貪心亂搞作風,開始枚舉了。
A: 字串暴力
B: 列舉暴力
C: 貪心列舉
D: 離散化列舉
讀懂題意需要一定的水平。

A

? 字元可以替換為任意字元,求方案使得串 abacaba 恰好出現一次。
暴力模擬題。
先對原串非 ? 字元匹配一遍,如果找到一個則將所有問號設定為 z 輸出,若超過一個則輸出 No,否則繼續。
帶上 ? 進行匹配,如果匹配,則將當前匹配的帶問號串設定為 abacaba 再對置換後的串進行嚴格掃描看出現次數,如果不超過 1 則是一種合法答案,否則回溯繼續列舉。
複雜度大概是 \(O(n^3)\)

?感覺卡滿會鍋,但似乎沒那麼容易卡滿。
然而我連WA兩發,在1h20min才過掉這道題……

#include <cstdio>
#include <algorithm>
#include <ctype.h>
const int bufSize = 1e6;
inline char nc()
{
    #ifdef DEBUG
    return getchar();
    #endif
    static char buf[bufSize], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
    static char c;
    for (; c != '?' && !isalpha(c); c = nc());
    for (; c == '?' || isalpha(c); c = nc()) *s++ = c;
    *s = '\0';
}
template<typename T>
inline T read(T &r)
{
    static char c;
    static int flag;
    flag = 1, r = 0;
    for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
    for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
    return r *= flag;
}
const int maxn = 60;
int T;
int n;
char s[maxn];
int save[maxn];
char t[maxn];
//abacaba
int main()
{
    t[1] = 'a',t[2] = 'b',t[3] = 'a',t[4] = 'c',t[5] = 'a',t[6] = 'b',t[7] = 'a';
    read(T);
    while(T--)
    {
        read(n);
        read(s + 1);
        int ans = 0;
        for(int i = 1;i<=n - 6;++i)
        {
            bool flag = 1;
            for(int j = 1;flag && j<=7;++j) if(!(s[i + j - 1] == t[j])) flag = 0;
            ans += flag;
        }
        if(ans > 1){puts("No");goto end;}
        if(ans == 1)
        {
            puts("Yes");
            for (int i = 1; i <= n; ++i) if(s[i] == '?') putchar('z'); else putchar(s[i]);
            putchar('\n');
            goto end;
        }
        for(int i = 1;i<=n;++i)
        {
            bool flag = 1;
            for(int j = 1;j<=7;++j) if(!(s[i+j-1] == t[j] || s[i+j-1] == '?')) flag = 0;
            if(flag)
            {
                for (int j = 1; j <= 7; ++j) save[i+j-1] = s[i+j-1],s[i + j - 1] = t[j];
                int cnt = 0;
                for(int k = 1;k<=n;++k)
                {
                    bool f = 1;
                    for(int p = 1;p<=7;++p) if(s[k+p-1] != t[p]) f = 0;
                    if(f) ++cnt;
                }
                if(cnt == 1)
                {
                    puts("Yes");
                    for(int k = 1;k<=n;++k) if(s[k] == '?') putchar('z'); else putchar(s[k]);
                    putchar('\n');
                    goto end;
                }
                for (int j = 1; j <= 7; ++j) s[i + j - 1] = save[i + j - 1];
            }
        }
        puts("No");
        end:;
    }
    return 0;
}

B

應該是本場最簡單的題了……然而也想了我20min。
求一個 \(m = n \times a + b - c\)\((a,b,c)\),其中 \(n\) 為正數。
一開始胡亂分析想找特解,失敗了之後,發現數據範圍可以列舉。
\(r - l \leq 10^5\),且 \(T \leq 20\),直接列舉 \(a\) 即可。
列舉 \(a\) 後,計算出 \(n \times a \leq m\)\(m \leq n \times a\) 對應的 \(n\),計算出 \(b - c\)的值,判斷 \(l - r \leq b - c \leq r - l\) 則有解,否則無解。

#include <cstdio>
#include <algorithm>
#include <ctype.h>
const int bufSize = 1e6;
inline char nc()
{
    #ifdef DEBUG
    return getchar();
    #endif
    static char buf[bufSize], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
    static char c;
    for (; !isalpha(c); c = nc());
    for (; isalpha(c); c = nc()) *s++ = c;
    *s = '\0';
}
template<typename T>
inline T read(T &r)
{
    static char c;
    static int flag;
    flag = 1, r = 0;
    for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
    for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
    return r *= flag;
}
#define int long long
int T;
long long l,r,m,n,a,b,c,na,bc,x;
inline bool check()
{
    na = n * a;
    bc = m - na;
    if(bc > x || bc < -x) return false;
    if(bc > 0) b = l + bc,c = l;
    else c = r,b = r + bc;
    return true;
}
signed main()
{
    read(T);
    while(T--)
    {
        read(l),read(r),read(m);
        x = r - l;
        for(a = l;a<=r;++a)
        {
            n = m / a;
            if(n && check())
            {
                printf("%lld %lld %lld\n",a,b,c);
                break;
            }
            ++n;
            if(check())
            {
                printf("%lld %lld %lld\n",a,b,c);
                break;
            }
        }
    }
    return 0;
}

C

場上把我卡到自閉的題目。
可以貪心+排序+列舉求解。
首先,只有一個元素會被取多次。否則的話,我們一定能重複取被取多次元素中 \(b\) 值最大的那個獲取更優解。
我們開兩個陣列: \(A\)\(B\),其中 \(A\)\(a\) 降序排序, \(B\)\(b\) 降序排序。
列舉 \(B\) 陣列的位置,找到 \(A_l.a > B_i.b\) 的最大 \(l\)
此時強制選 \(B_i\) 一個後,取 \(A\) 陣列 \([1,l]\),剩下的額度全取 \(B_i\) 即是當前最優解。
依次列舉,即可獲得所有的可能解。
不難發現,過程中 \(l\) 單調遞增,因此複雜度是線性的。算上排序,複雜度 \(O(n \log n)\),可以通過本題。
細節較多,注意判斷邊界,特判全部元素只取一次的解。

#include <cstdio>
#include <algorithm>
#include <ctype.h>
const int bufSize = 1e6;
inline char nc()
{
    #ifdef DEBUG
    return getchar();
    #endif
    static char buf[bufSize], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
    static char c;
    for (; !isalpha(c); c = nc());
    for (; isalpha(c); c = nc()) *s++ = c;
    *s = '\0';
}
template<typename T>
inline T read(T &r)
{
    static char c;
    static int flag;
    flag = 1, r = 0;
    for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
    for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
    return r *= flag;
}
const int maxn = 1e5 + 100;
struct node
{
    unsigned long long a,b,id;
}A[maxn],B[maxn];
bool cmp(const node &x,const node &y){return x.a > y.a;}
bool cmp2(const node &x,const node &y){return x.b > y.b;}
unsigned long long sum[maxn];
int T,n,m;
bool vis[maxn];
int main()
{
    read(T);
    while(T--)
    {
        read(m),read(n);
        for (int i = 1; i <= n; ++i) read(A[i].a), read(A[i].b), B[i].a = A[i].a, B[i].b = A[i].b, A[i].id = B[i].id = i;
        std::sort(A + 1, A + 1 + n, cmp), std::sort(B + 1, B + 1 + n, cmp2);
        for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + A[i].a, vis[i] = 0;
        unsigned long long ans = 0;
        if (m <= n) ans = sum[m];
        int l = 1;
        for (int i = 1; i <= n; ++i)
        {
            while (l <= n && l <= m && A[l].a > B[i].b) vis[A[l].id] = 1, ++l;
            if(l == m + 1)
            {
                if (vis[B[i].id]) ans = std::max(ans, sum[m]);
                else if (!vis[B[i].id]) ans = std::max(ans, sum[m - 1] + B[i].a);
                break;
            }
            if(l == n + 1)
            {
                ans = std::max(ans,sum[n] + (m - n) * B[i].b);
                break;
            }
            if (vis[B[i].id] && m - l + 1 >= 0) ans = std::max(ans, sum[l - 1] + (m - l + 1) * B[i].b);
            else if (!vis[B[i].id] && m - l >= 0) ans = std::max(ans, sum[l - 1] + (m - l) * B[i].b + B[i].a);
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D

這題的難點主要在於讀懂題意,晦澀難懂的英語和混亂不清的邊界實在是讓人痛苦萬分。
題目中到達時間只需要考慮分鐘,小時完全無用。
即選定一個 \(t\) 後,在 \((t - K,t)\)\((t + half - K,t + half)\) 中的元素全部不合法。
此外,固定給了一個整點發的車,因此 \([M - K,M-1]\) 也是不合法的。
轉化為閉區間,\([t - K + 1,t - 1]\)\([t + half - K + 1,t + half - 1]\)\([M - K,M - 1]\)
個人習慣讓離散化前的數值都有嚴格對應的新值,這樣似乎不容易出鍋。
那麼對於離散化前的數值 \(x\),將 \(x - 1\)\(x - K + 1\) 同時插入即可。
隨後開個桶記錄每個位置出現元素次數,做一遍字首和方便求區間元素數,列舉 \(t\) 求解即可。
邊界、細節需要多加註意。我的實現有些冗雜。

#include <cstdio>
#include <algorithm>
#include <ctype.h>
const int bufSize = 1e6;
#define DEBUG
inline char nc()
{
    #ifdef DEBUG
    return getchar();
    #endif
    static char buf[bufSize], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, bufSize, stdin), p1 == p2) ? EOF : *p1++;
}
inline void read(char *s)
{
    static char c;
    for (; !isalpha(c); c = nc());
    for (; isalpha(c); c = nc()) *s++ = c;
    *s = '\0';
}
template<typename T>
inline T read(T &r)
{
    static char c;
    static int flag;
    flag = 1, r = 0;
    for (c = nc(); !isdigit(c); c = nc()) if (c == '-') flag = -1;
    for (; isdigit(c); c = nc()) r = r * 10 + c - 48;
    return r *= flag;
}
template<typename T>
inline T abs(const T &a){return a > 0 ? a : -a;}
const int maxn = 1e5 + 100;
int n,H,M,K,half;
int num[maxn*6],tot;
int h[maxn],m[maxn];
int sum[maxn*6];
inline void add(int x)
{
    num[++tot] = x;
    num[++tot] = x - 1;
    num[++tot] = x - K + 1;
}
int main()
{
    read(n),read(H),read(M),read(K);
    half = M / 2;
    num[++tot] = -1,num[++tot] = 0,num[++tot] = M - 1;
    for (int i = 1; i <= n; ++i) 
    {
        read(h[i]), read(m[i]);
        if(m[i] >= half) m[i] -= half;
        add(m[i]),add(m[i] + half);
    }
    std::sort(num + 1,num + 1 + tot);
    tot = std::unique(num + 1,num + 1 + tot) - num - 1;
    for (int i = 1; i <= n; ++i)
        m[i] = std::lower_bound(num + 1, num + 1 + tot, m[i]) - num, sum[m[i]]++;
    for (int i = 1; i <= tot; ++i)  sum[i] += sum[i-1];
    int ans = 1<<30,anst = 0;
    int end = std::lower_bound(num + 1,num + 1 + tot,M - 1) - num;
    int es = std::lower_bound(num + 1,num + 1 + tot,M - K) - num;
    int eans = sum[end] - sum[es - 1];
    for (int i = 1; i <= tot; ++i) 
    {
        int t = num[i];
        if(t < 0) continue;
        if(t >= half) break;
        if(t >= K)
        {
            //[t - K + 1,t - 1] + [t + half - K + 1,t + half - 1] + [M - K,M - 1]
            int l1 = t - K + 1,r1 = t - 1;
            int l2 = t + half - K + 1,r2 = t + half - 1;
            l1 = std::lower_bound(num + 1,num + 1 + tot,l1) - num;
            r1 = std::lower_bound(num + 1,num + 1 + tot,r1) - num;
            l2 = std::lower_bound(num + 1,num + 1 + tot,l2) - num;
            r2 = std::lower_bound(num + 1,num + 1 + tot,r2) - num;
            int tans = eans + sum[r2] - sum[l2 - 1] + sum[r1] - sum[l1 - 1];
            if(tans < ans) ans = tans,anst = i;
        }
        else
        {
            //[,t - 1] + [t + half - K + 1,t + half - 1] + [M - K,M - 1]
            int r1 = t - 1;
            int l2 = t + half - K + 1,r2 = t + half - 1;
            r1 = std::lower_bound(num + 1,num + 1 + tot,r1) - num;
            l2 = std::lower_bound(num + 1,num + 1 + tot,l2) - num;
            r2 = std::lower_bound(num + 1,num + 1 + tot,r2) - num;
            int tans = eans + sum[r1] + sum[r2] - sum[l2 - 1];
            if(tans < ans) ans = tans,anst = i;
        }
    }
    printf("%d %d\n",ans,num[anst]);
    int t = num[anst];
    if(t >= K)
    {
        int l1 = t - K + 1, r1 = t - 1;
        int l2 = t + half - K, r2 = t + half - 1;
        l1 = std::lower_bound(num + 1, num + 1 + tot, l1) - num;
        r1 = std::lower_bound(num + 1, num + 1 + tot, r1) - num;
        l2 = std::lower_bound(num + 1, num + 1 + tot, l2) - num;
        r2 = std::lower_bound(num + 1, num + 1 + tot, r2) - num;
        for (int i = 1; i <= n; ++i) 
            if((m[i] >= l1 && m[i] <= r1) || (m[i] >= l2 && m[i] <= r2) || (m[i] >= es && m[i] <= end)) 
                printf("%d\n",i);
    }
    else
    {
        int r1 = t - 1;
        int l2 = t + half - K + 1, r2 = t + half - 1;
        r1 = std::lower_bound(num + 1, num + 1 + tot, r1) - num;
        l2 = std::lower_bound(num + 1, num + 1 + tot, l2) - num;
        r2 = std::lower_bound(num + 1, num + 1 + tot, r2) - num;
        for (int i = 1; i <= n; ++i)
            if ((m[i] <= r1) || (m[i] >= l2 && m[i] <= r2) || (m[i] >= es && m[i] <= end))
                printf("%d\n", i);
    }
    return 0;
}