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;
}