2019 XII Samara Regional Intercollegiate Programming Contest
A. Rooms and Passages
题意:
有 n + 1 n+1 n+1 个房间, n n n 把钥匙,有第 i i i 把钥匙你可以从房间 i − 1 i-1 i−1 走到房间 i i i。每个房间 i i i 都有两种类型中的一种:
- 一、只有拥有钥匙 a i a_i ai 才能从 i − 1 i-1 i−1 走到 i i i。
- 二、从 i − 1 i-1 i−1 走到 i i i 之后会失去钥匙 a i a_i ai 。
现在问你从每个房间出发开始往后走,最远能走几个房间。
题解:
尺取(two pointers),根据走到房间的种类:如果是种类一,则判断所需钥匙数量是否为 1;如果是种类二,则增加当前房间消耗的钥匙数量,并且左端点退出时减少被消耗的钥匙数量。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=500005;
int a[maxn];
int type[maxn];
int tag[maxn];
int ans[maxn];
int main(){
int n;scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
if(x>0) a[i]=x,type[i]=1;
else a[i]=-x,type[i]=2;
}
int ed=1;
for(int st=1;st<=n;st++){
while(ed<=n){
if(type[ed]==1){
if(tag[a[ed]]) break;
}else{
tag[a[ed]]++;
}
ed++;
}
ans[st]=ed-st;
if(type[st]==2) tag[a[st]]--;
}
for(int i=1;i<=n;i++) printf("%d%c",ans[i],(i==n)?'\n':' ');
return 0;
}
B. Rearrange Columns
题意:
给定一个 2 ∗ l e n 2*len 2∗len 的点阵,每个点均为 . . . 或 # \# #,问能否将点阵的每一列重排,使得所有的 # \# # 构成一个联通块。
题解:
显然我们可以分析出,每一列只有第一行是 # \# # 的都可以放到一起,形如:
######
......
同理每一列只有第二行是 # \# # 的也都可以放到一起,形如:
......
######
而这两种情况同时出现的情况下,我们只需要存在某一列两行都是 # \# #,我们就可以把他们拼接成如下形式:
#######......
......#######
这样就成功将所有 # \# # 放到了同一个联通块中。
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1005;
char s[2][maxn];
int cnt[4];
int main(){
scanf("%s%s",s[0],s[1]);
int len=(int)strlen(s[0]);
for(int i=0;i<len;i++){
if(s[0][i]=='#'&&s[1][i]=='.') cnt[0]++;
else if(s[0][i]=='#'&&s[1][i]=='#') cnt[1]++;
else if(s[0][i]=='.'&&s[1][i]=='#') cnt[2]++;
else if(s[0][i]=='.'&&s[1][i]=='.') cnt[3]++;
}
if(cnt[0]&&cnt[2]&&!cnt[1]) printf("NO\n");
else{
printf("YES\n");
for(int j=0;j<cnt[0];j++) printf("#");
for(int j=0;j<cnt[1];j++) printf("#");
for(int j=0;j<cnt[2];j++) printf(".");
for(int j=0;j<cnt[3];j++) printf(".");
printf("\n");
for(int j=0;j<cnt[0];j++) printf(".");
for(int j=0;j<cnt[1];j++) printf("#");
for(int j=0;j<cnt[2];j++) printf("#");
for(int j=0;j<cnt[3];j++) printf(".");
printf("\n");
}
return 0;
}
C. Jumps on a Circle
题意:
有 p p p 个点 0 0 0 到 p − 1 p-1 p−1 围成一圈(即对于 1 < i < n 1 < i < n 1<i<n, i i i 的左边为 i − 1 i-1 i−1, i i i 的右边为 i + 1 i+1 i+1, 1 1 1 的左边为 n n n, n n n 的右边为 1 1 1),一个人初始站在 0 0 0,第一次跳 1 1 1 步,第二次跳 2 2 2 步,第 i i i 次跳 i i i 步,共跳 n n n 次,问总共有多少个点被跳到过(包括初始站过的点 0 0 0)。
题解:
数据给出的 n n n 大到 1 0 18 10^{18} 1018,当然我们不能全部跳完,所以我们思考会不会在某个地方出现循环节。而这种情况下的循环节要求到达同一个地方之后的所有行动都要一致。找规律可以发现当 p p p 为奇数时,跳跃 p p p 次即可找到循环节,思考原因我们可以发现,跳跃 p p p 次我们总共跳跃的步数是 ( 1 + p ) ∗ p 2 \frac{(1+p)*p}{2} 2(1+p)∗p ,其中 1 + p 1+p 1+p 为偶数,即 ( 1 + p ) % 2 = 0 (1+p)\%2=0 (1+p)%2=0,则 ( 1 + p ) ∗ p 2 % p = 0 \frac{(1+p)*p}{2}\%p=0 2