[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;
}
posted @ 2020-07-19 21:55  *Clouder  阅读(355)  评论(0编辑  收藏  举报