Codeforces Round #578 (Div. 2)

A. Hotelier

模拟

#include<cstdio>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
const int N=1e6+5;
char str[N];
set<int> s;
int main(){
    int n;
    scanf("%d %s",&n,str);
    for(int i=0;i<10;i+=1) s.insert(i);
    set<int>::iterator it;
    for(int i=0;i<n;i+=1){
        if(str[i]=='L')
            s.erase(s.begin());
        else if(str[i]=='R'){
            it=s.end();
            it--;
            s.erase(it);
        }
        else
            s.insert(str[i]-'0');
    }
    for(int i=0;i<10;i+=1) printf("%d",s.count(i)?0:1);
    printf("\n");
    return 0;
}

B. Block Adventure

题意:告诉你n阶台阶的高度,台阶由石块叠起,当前台阶到下一台阶的前提是当前台阶到下一台阶的高度差<=k,初始时角色位于台阶1,拥有石块m,石块的作用是使用后可以增加台阶的高度,每个角色离开台阶i后可以收取台阶i上的石块,但前提是保证|hi-h[i+1]|<=k,问你角色能否到达台阶n

思路:贪心,每一步收取尽量多的石块并加以利用,假设当前位置处于i,那么到下一位置的最低高度要求是h=max(0,h[i+1]-k),如果当前高度+手中拥有的石块数都<h,那么角色到不了台阶n,否则,收取当前台阶多出的石块数并走到下一阶

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 2e2 + 5;
int A[N];
int main() {
    int T;
    scanf("%d", &T);
    while(T--) {
        int n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        for(int i = 1; i <= n; i += 1)
            scanf("%d", &A[i]);
        bool res = true;
        for(int i = 1; i < n; i += 1) {
            int need = max(0, A[i + 1] - k);
            if(A[i] + m < need) {
                res = false;
                break;
            } else if(A[i] + m >= need)
                m += A[i] - need;
        }
        printf("%s\n", res ? "YES" : "NO");
    }
    return 0;
}

C. Round Corridor

规律

#include<cstdio>
#include<algorithm>
using namespace std;
int main(){
    long long n,m,sx,sy,ex,ey,A,B;
    int Q;
    scanf("%lld%lld%d",&n,&m,&Q);
    long long gcd=__gcd(n,m);
    n/=gcd;
    m/=gcd;
    while(Q--)
    {
        scanf("%lld%lld%lld%lld",&sx,&sy,&ex,&ey);
        --sy;
        --ey;
        if(sx==1) A=sy/n;
        else A=sy/m;
        if(ex==1) B=ey/n;
        else B=ey/m;
        printf("%s\n",A==B?"YES":"NO");
    }
    return 0;
}

D. White Lines( 滑动窗口 )

题意:给你一个n*n的矩阵,矩阵内只有两种元素,B和W,现让你选择一个k*k的子矩阵,使得该子矩阵内的元素都变为W,问怎么选择子矩阵能使最终矩阵内行列元素都为W的情况最多

思路:预处理出每行每列第一个和最后一个出现B的位置,若当前行/列没有B出现,则答案+1

之后从上到下遍历行,对于当前第i~i+k-1行,每一行要想涂白,那么擦去的区间应该是[第一个‘B’出现的 位置,最后一个'B'出现的位置],前提是该区间长度<k,分别记录两个边界的下标(目的是在之后遍历列的过程中可以记录行的贡献)。之后遍历每一列,统计最多能被涂白的列数。怎么统计?每一列是否能被涂白就看该列上区间[第一个‘B’出现的 位置,最后一个'B'出现的位置]是否在区间[i,i+k-1]内,在从左往右遍历列的过程中,其实是一个长度为K滑窗的过程,加上新加入窗口的列的贡献,减去出去的列贡献,最后更新答案

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 2e3 + 5;
char str[N];
int main() {
    int n, k;
    scanf("%d%d", &n, &k);
    vector<int> rowf(n, -1), rowl(n, -1);
    vector<int> colf(n, -1), coll(n, -1);
    for(int i = 0; i < n; ++i) {
        scanf("%s", str);
        for(int j = 0; j < n; ++j) {
            if(str[j] == 'B') {
                if(colf[j] == -1) colf[j] = i;
                coll[j] = i;
                if(rowf[i] == -1) rowf[i] = j;
                rowl[i] = j;
            }
        }
    }
    int res = 0;
    for(int i = 0; i < n; ++i) {
        if(rowf[i] == -1) res += 1;
        if(colf[i] == -1) res += 1;
    }
    int most = 0;
    for(int i = 0; i + k <= n; ++i) {
        int now = res;
        vector<int> gain(n, 0);
        vector<int> loss(n, 0);
        for(int j = i; j < i + k; ++j) {
            if(rowf[j] == -1)
                continue;
            if(rowl[j] - rowf[j] < k) {
                gain[rowl[j]] += 1;
                loss[rowf[j]] += 1;
            }
        }
        for(int j = 0; j < n; ++j) {
            now += gain[j];
            if(colf[j] >= i && coll[j] < i + k)
                now += 1;
            if(j >= k) {
                now -= loss[j - k];
                if(i <= colf[j - k] && coll[j - k] < i + k)
                    --now;
            }
            most = max(most, now);
        }
    }
    printf("%d\n", most);
    return 0;
}

E. Compress Words

题意:给你n个字符串,将这些字符串从左到右尾首相连,如果第i个串的后缀和第i+1个串的前缀存在相同部分,那么在连接串的时候公共部分只保留一个,输出最终串

思路:KMP找出相同前后缀的长度,不断更新答案串

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;
int nex[N];
char S[N], T[N];
void getNext(char *T, int l) {
    int i = 0;
    int k = -1;
    nex[0]=-1;
    while(i < l) {
        if(k == -1 || T[i] == T[k]) {
            i += 1;
            k += 1;
            nex[i] = k;
        } else
            k = nex[k];
    }
}
int KMP(int n, int k, int l) {
    int p = 0;
    for(int i = k; i < n + k; i += 1) {
        while(p > 0) {
            if(S[i] != T[p])
                p = nex[p];
            else
                break;
        }
        if(S[i] == T[p])
            p += 1;
        if(p == l)
            return n;
    }
    return p;
}
int main() {
    int n, l, len;
    scanf("%d", &n);
    for(int i = 0; i < n; i += 1) {
        scanf("%s", T);
        l = strlen(T);
        if(!i) {
            len = l;
            for(int k = 0; k < l; k += 1) {
                S[k] = T[k];
            }
        } else {
            getNext(T, l);
            int p = KMP(min(len, l), max(0, len - l), l);
            for(int k = p; k < l; k += 1, len += 1) {
                S[len] = T[k];
            }
        }
    }
    S[len]='\0';
    printf("%s\n",S);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值